1. 程式人生 > >countDownLatch.await()與thread.join()區別

countDownLatch.await()與thread.join()區別

在編寫多執行緒的工作中,有個常見的問題:主執行緒(main) 啟動好幾個子執行緒(task)來完成併發任務,主執行緒要等待所有的子執行緒完成之後才繼續執行main的其它任務。

預設主執行緒退出時其它子執行緒不會停,如果想讓main退出時其它子執行緒終止,可以用subThread.setDaemon(true) 設定子執行緒為“守護執行緒”。

如果要在主執行緒等待所有子執行緒完成後,還要執行其它操作(比如:結果合併).可以用join()方法來等待所有子執行緒完成後,才繼續執行。如下:

例項1:Join實現

複製程式碼

public class TestRunnable implements Runnable{

    /** 執行緒名 */
    private String threadName;


    public TestRunnable(String threadName) {
        this.threadName = threadName;
    }


    @Override
    public void run() {
        System.out.println( "[" + threadName + "] Running !" );
    }

    public static void main(String[] args) throws InterruptedException {
        List<Thread> lists = new ArrayList<Thread>();
        for(int i=0; i<5; i++){
            Thread thread = new Thread(new TestRunnable("子執行緒" + (i + 100)));
            lists.add(thread);
            thread.start();
        }
        System.out.println("主執行緒阻塞,等待所有子執行緒執行完成");
        for(Thread thread : lists){
            // 如果註釋掉thread.join(),啟動後 main執行緒 與 所有子執行緒 thread併發工作,並不會等待子執行緒完成後再執行
            thread.join();
        }
        System.out.println("所有執行緒執行完成!");
    }
}

複製程式碼

此外可以用java.util.concurrent.CountDownLatch類更簡潔的實現這種場景.

CountDownLatch 的作用和 Thread.join() 方法類似,可用於一組執行緒和另外一組執行緒的協作。

例如:主執行緒在做一項工作之前需要一系列的準備工作,只有這些準備工作都完成,主執行緒才能繼續它的工作,這些準備工作彼此獨立,所以可以併發執行以提高速度。在這個場景下就可以使用 CountDownLatch 協調執行緒之間的排程了。

在直接建立執行緒的年代(Java 5.0 之前),我們可以使用 Thread.join().在 JUC 出現後,因為執行緒池中的執行緒不能直接被引用,所以就必須使用 CountDownLatch 了。

CountDownLatch 是能使一組執行緒等另一組執行緒都跑完了再繼續跑 ,CountDownLatch.await() 方法在倒計數為0之前會阻塞當前執行緒.

例項2:CountDownLatch實現

複製程式碼

public class TestRunnable implements Runnable{

    /** 處理main執行緒阻塞(等待所有子執行緒) */
    private CountDownLatch countDown;

    /** 執行緒名字 */
    private String  threadName;


    public TestRunnable(CountDownLatch countDownLatch, String threadName) {
        this.countDown = countDownLatch;
        this.threadName = threadName;
    }

    @Override
    public void run() {
        System.out.println( "[" + threadName + "] Running ! [countDownLatch.getCount() = " + countDown.getCount() + "]." );
        // 每個獨立子執行緒執行完後,countDownLatch值減1
        countDown.countDown();
    }

    public static void main(String [] args) throws InterruptedException {
        int countNum = 5;
        CountDownLatch countDownLatch = new CountDownLatch(countNum);
        for(int i=0; i<countNum; i++){
            new Thread(new TestRunnable(countDownLatch,"子執行緒" + (i+100))).start();
        }
        System.out.println("主執行緒阻塞,等待所有子執行緒執行完成");
        //endLatch.await()使得主執行緒(main)阻塞直到endLatch.countDown()為零才繼續執行
        countDownLatch.await();
        System.out.println("所有執行緒執行完成!");
    }
}

複製程式碼

執行結果:

對於countDownLatch我們需要注意:CountDownLatch.await() 方法在倒計數為0之前會阻塞當前執行緒.
a)在不使用await()的時候:

執行結果如下:

此時我們看到,主執行緒和子執行緒併發執行,主執行緒執行完後子執行緒還在執行,沒有實現我們需要的場景.

b)CountDownLatch.await() 方法在倒計數不為0時

執行結果:

如圖,可以看到,子執行緒執行完成了,但是countDownLatch的倒記數的值不為0,進入持續等待中,並沒有喚醒主執行緒來執行.所以countDownLatch.await()生效必須保證計數值變為0.

轉載自:[http://www.cnblogs.com/dennisit/p/4340611.html]