java 線程 --- Thread,Runnable,Callable 基礎學習

來源:互聯網
上載者:User

標籤:三種方式   執行個體   class   dcl   this   parent   getname   多線程   cpu   

java 使用 Thread 類代表線程,所有現場對象都必須是 Thread 類或者其子類的執行個體。每個線程的作用是完成一定的任務,實際上就是執行一段程式流。java 使用線程執行體來代表這段程式流。

1.繼承Thread 類建立線程

啟動多線程的步驟如下:
(1)定義Thread 類的子類,並重寫該類的run() 方法,該run() 方法的方法體就代表類線程需要完成的任務。因此把run() 方法稱為線程執行體。
(2)建立 Thread 子類的執行個體,即建立線程對象。
(3)調用線程的star()方法來啟動該線程。

相關代碼如下:

/**     * 繼承 thread 的內部類,以買票例子     */    public class FirstThread extends Thread{        private int i;        private int ticket = 10;        @Override        public void run() {            for (;i<20;i++) {                //當繼承thread 時,直接使用this 可以擷取當前的線程,getName() 擷取當前線程的名字//                Log.d(TAG,getName()+" "+i);                if(this.ticket>0){                    Log.e(TAG, getName() + ", 賣票:ticket=" + ticket--);                }            }        }    }    private void starTicketThread(){        Log.d(TAG,"starTicketThread, "+Thread.currentThread().getName());        FirstThread thread1 = new FirstThread();        FirstThread thread2 = new FirstThread();        FirstThread thread3 = new FirstThread();        thread1.start();        thread2.start();        thread3.start();        //開啟3個線程進行買票,每個線程都賣了10張,總共就30張票    }

運行結果:

可以看到 3 個線程輸入的 票數變數不連續,注意:ticket 是 FirstThread 的執行個體屬性,而不是局部變數,但是因為程式每次建立線程對象都需要建立一個FirstThread 的對象,所有多個線程不共用該執行個體的屬性。

2.實現 Runnable 介面建立線程

注意:public class Thread implements Runnable

(1)定義 Runnable 介面的實作類別,並重寫該介面的run()方法,該run() 方法的方法體同樣是該線程的線程執行體。
(2)建立 Runnable 執行個體類的執行個體,此執行個體作為 Thread 的 target 來建立Thread 對象,該Thread 對象才是真正的對象。

相關代碼如下:

/**     * 實現 runnable 介面,建立線程類     */    public class SecondThread implements Runnable{        private int i;        private int ticket = 100;        @Override        public void run() {            for (;i<20;i++) {                //如果線程類實現 runnable 介面                //擷取當前的線程,只能用 Thread.currentThread() 擷取當前的線程名                Log.d(TAG,Thread.currentThread().getName()+" "+i);                if(this.ticket>0){                    Log.e(TAG, Thread.currentThread().getName() + ", 賣票:ticket=" + ticket--);                }            }        }    }    private void starTicketThread2(){        Log.d(TAG,"starTicketThread2, "+Thread.currentThread().getName());        SecondThread secondThread = new SecondThread();        //通過new Thread(target,name)建立新的線程        new Thread(secondThread,"買票人1").start();        new Thread(secondThread,"買票人2").start();        new Thread(secondThread,"買票人3").start();        //雖然是開啟了3個線程,但是一共只買了100張票    }

運行結果:

可以看到 3 個線程輸入的 票數變數是連續的,採用 Runnable 介面的方式建立多個線程可以共用線程類的執行個體的屬性。這是因為在這種方式下,程式所建立的Runnable 對象只是線程的 target ,而多個線程可以共用同一個 target,所以多個線程可以共用同一個線程類(實際上應該是該線程的target 類)的執行個體屬性。

3.使用 Callable 和Future 建立線程

從 java 5 開始,Java 提供了 Callable 介面,該介面是runnable 的增強版,Callable 提供類一個 call() 方法可以作為線程執行體,但是call() 方法的功能更強大。
(1) call() 方法可以有傳回值
(2) call() 方法可以聲明拋出異常

因此我們完全可以提供一個callable 對象作為Thread的 target ,而該線程的執行體就是該callable 對象的call() 方法。同時 java 5 提供了 Future 介面 來代表Callable 介面裡 call() 方法的傳回值,並且提供了一個 futureTask 的實作類別,該實作類別實作類別 future 介面,並實現了runnable 介面—可以作為Thread 類的target.

啟動步驟如下:
(1)建立callable介面的實作類別,並實現call() 方法,該call() 方法將作為線程的執行體,且該call() 方法是有傳回值的。
(2)建立 callable實作類別的執行個體,使用 FutureTask 類來封裝Callable對象,該FutureTask 對象封裝 call() 方法的傳回值。
(3)使用FutureTask 對象作為Thread對象的target建立並啟動新線程。
(4)調用FutureTask對象的get()方法來擷取子線程執行結束後的傳回值。

相關代碼如下:

/**     * 使用callable 來實現線程類     */    public class ThirdThread implements Callable<Integer>{        private int ticket = 20;        @Override        public Integer call(){            for ( int i = 0;i<10;i++) {                //擷取當前的線程,只能用 Thread.currentThread() 擷取當前的線程名//                Log.d(TAG,Thread.currentThread().getName()+" "+i);                if(this.ticket>0){                    Log.e(TAG, Thread.currentThread().getName() + ", 賣票:ticket=" + ticket--);                }            }            return ticket;        }    }    private void starCallableThread(){        ThirdThread thirdThread = new ThirdThread();        FutureTask<Integer> task = new FutureTask<Integer>(thirdThread);        new Thread(task,"有傳回值的線程").start();        try {            Integer integer = task.get();            Log.d(TAG,"starCallableThread, 子線程的傳回值="+integer);        } catch (InterruptedException e) {            e.printStackTrace();        } catch (ExecutionException e) {            e.printStackTrace();        }    }

運行結果:

注意:Callable的call() 方法允許聲明拋出異常,並且允許帶有傳回值。
程式最後調用FutureTask 對象的get()方法來返回Call()方法的傳回值,導致主線程被阻塞,直到call()方法結束並返回為止。

4.三種方式的對比
採用繼承Thread 類的方式建立多線程
劣勢: 已經繼承Thread類不能再繼承其他父類。
優勢: 編寫簡單
採用繼承Runnable,Callable 介面的方式建立多線程
劣勢: 編程稍微有點複雜,如果需要訪問當前線程必須使用Thread.currentThread()
優勢:
(1)還可以繼承其他類
(2)多個線程可以共用一個target 對象,所以非常適合多個相同的線程來處理同一份資源的情況,從而將cpu,代碼和資料分開,形成清晰的模型,較好的體現類物件導向的思想。

java 線程 --- Thread,Runnable,Callable 基礎學習

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.