如果現在有五個線程A、B、C、D、E,請問如何用E線程用於統計A、B、C、D四個線程的結果?
題意需要用E線程統計A、B、C、D四個線程,也就是說E線程必須要等到前面四個線程運行結束之後才能執行。那麼如何使用E線程來統計前面四個線程的結果呢?
下面介紹兩種實現方法:
一、CountDownLatch
CountDownLatch是一種java.util.concurrent包下一個同步工具類,它允許一個或多個線程等待直到在其他線程中一組操作執行完成。
我們可以將CountDownLatch看作一個計數器,可以給定其一個數來對其進行初始化。當它計數的值變爲0時,纔可以執行後續的步驟。
1
|
public CountDownLatch( int count) |
CountDownLatch有兩個方法是我們常用的:await();和countDown();
await()函數用於阻塞當前線程直到CountDownLatch的計數值變爲0;
countDown()方法用於將當前CountDownLatch的計數值減1;
簡單的介紹了CountDownLatch的作用和用法之後,我們看下如下的代碼:
import java.util.concurrent.CountDownLatch; public class Test1{ public static void main(String[] args) throws InterruptedException { int number = 3; CountDownLatch latch = new CountDownLatch(number); for(int i=0;i<3;i++){ ThreadDemo demo = new ThreadDemo(latch); demo.start(); System.out.println(i); } latch.await(); System.out.println("Check it Out"); } } class ThreadDemo extends Thread{ private CountDownLatch latch; public ThreadDemo(CountDownLatch latch){ this.latch = latch; } public void run(){ try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } latch.countDown(); } }
我們首先創建了一個CountDownLatch對象給定一個number將其初始化,需要注意的是當前CountDownLatch對象的值減爲0之後,該對象便失去了作用。
同時我們提供了繼承自Thread的ThreadDemo類,它包含了一個CountDownLatch對象。當我們創建ThreadDemo對象時,會將CountDowmLatch對象注入其中。
在TreadDemo類的run()方法中,我們執行了兩步動作:延遲5秒後將CountDownLatch的計數值減1。
在main函數中,我們利用for循環創建了三個ThreadDemo對象並執行他們的run()方法。添加和不添加latch.await()語句將會得到兩種執行結果。
1、添加latch.await()語句
執行結果:
0
1
2
Check it Out
我們會發現0 1 2幾乎是同時打印出來的,但是“Check it Out”大概等待了5秒左右的時間後纔打印,這說明了其實“Check it Out”是等待前面三個線程執行完countDown()方法之後將latch的值減爲0之後才執行的。
2、無latch.await()語句
執行結果:
0
1
2
Check it Out
觀察運行結果我們會發現,0 1 2和“Check it Out”幾乎是同時出現的,也就是說“Check it Out”並沒有等待前面三個線程執行完成之後才執行。
CountDowLlatch的用途即是讓一組線程先執行,知道countdownlatch對象的計數值到0後,讓後續的線程繼續執行。
二、join方法實現
事實上,join方法也可以實現同樣的效果!代碼如下:
import java.util.concurrent.CountDownLatch; public class Test{ public static void main(String[] args) throws InterruptedException { int number = 3; CountDownLatch latch = new CountDownLatch(number); ThreadDemo thread1 = new ThreadDemo(1); ThreadDemo thread2 = new ThreadDemo(2); thread1.start(); thread1.join(); System.out.println("----"); thread2.start(); thread2.join(); System.out.println("main is end!"); } } class ThreadDemo extends Thread{ private int number; public ThreadDemo(int number){ this.number = number; } public void run(){ System.out.println("Thread"+number+" is running"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread"+number+"is end"); } }
運行結果如下:
Thread1 is running
Thread1is end
----
Thread2 is running
Thread2is end
main is end!
可以看到thread1阻塞了thread2,只有當thread1和thread2均執行結束後,main方法才能繼續執行。如果想要thread1和thread2同時執行的話,只需要做如下簡單的變動。
thread1.start();
thread1.join();
thread2.start();
thread2.join();
System.out.println("main is end!");
運行結果如下:
Thread1 is running
Thread2 is running
Thread1is end
Thread2is end
main is end!
因此,如果我們需要某些線程等待指定任務執行完畢之後執行,選擇join方法也是一種選擇;