天天看點

CountDownLatch和join兩種方式實作多線程并行處理

上一篇部落客要介紹了如何使用Semaphore串行列印ABC,這一篇将介紹如何并行列印ABC,功能如下:

有三個線程,線程1列印A,線程2列印B,線程3列印C,這裡假設是必須做完A、B、C,才能根據A、B、C的結果去處理D,其中A、B、C可以并行執行,互不影響。這種情況很常見,比如我們泡茶,需要燒開水、準備茶葉、清洗茶具,最後是泡茶,可以将燒開水、準備茶葉、清洗茶具看成事件A、B、C,将最後的泡茶看成事件D,前面三件事是可以 并行發生的(燒開水的同時可以去準備茶葉,然後清洗茶具,這樣可以大大節約時間),但是必須前面三件事都完成了才能泡茶(事件D)。

下面通過join實作:

public class ConcurrentABCD {

    public static void main(String[] args) {
        Thread t1 = new Thread(new Print("A"));
        Thread t2 = new Thread(new Print("B"));
        Thread t3 = new Thread(new Print("C"));

        t1.start();
        t2.start();
        t3.start();
        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("D");    
    }

}

class Print implements Runnable {

    private String value;

    Print(String value) {
        this.value = value;
    }

    @Override
    public void run() {
        System.out.print(value);
    }   
}
           

上面的程式如果不加

t1.join();
t2.join();
t3.join();
           

這幾句,則由于各線程的執行是由CPU配置設定時間的,執行結果不可知,不能保證執行完ABC之後再列印D,join的功能是調用join的目前線程執行結束才能繼續執行其他線程。

下面通過concurrent包下的CountDownLatch來實作這種功能。

簡單介紹一下CountDownLatch,它的主要功能是保證一個或者多個線程等待,直到一組線程全部結束。jdk api描述如下:

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

它有兩個重要的方法await()和countDown(),await()是阻塞式的,表示目前線程必須等待直到通過countDown()使它的CountDownLatch 減為0為止,每調用一次countDown(),CountDownLatch 減1,jdk api描述如下:

A CountDownLatch is initialized with a given count. The await methods block until the current count reaches zero due to invocations of the countDown() method, after which all waiting threads are released and any subsequent invocations of await return immediately. This is a one-shot phenomenon – the count cannot be reset. If you need a version that resets the count, consider using a CyclicBarrier.

大意就是我上面說的,最後一句是說CountDownLatch是不能重置的(比如你原來設定的是3,當三個線程都運作完并且調用了countDown(),此時CountDownLatch 減為0,不能再恢複為3),如果需要使用可重置的,使用CyclicBarrier。

下面是代碼:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchABCD {

    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch();//初始化為需要等待3個線程完成

        Thread t1 = new Thread(new Print("A", latch));
        Thread t2 = new Thread(new Print("B", latch));
        Thread t3 = new Thread(new Print("C", latch));

        t1.start();
        t2.start();
        t3.start();

        try {
            latch.await();//阻塞,一直到CountDownLatch減為0為止
            System.out.println("D");//ABC都已結束,列印D

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

class Print implements Runnable {

    private String value;
    private CountDownLatch latch;

    Print(String value, CountDownLatch latch) {
        this.value = value;
        this.latch = latch;
    }

    @Override
    public void run() {
        System.out.print(value);
        latch.countDown();//目前線程運作結束,調用countDown将latch減1
    }
}
           

繼續閱讀