我先開多個線程處理數據,最後對這多個線程處理的結果,由一個線程進行彙總處理。那麼有一個問題就是,最後這個線程要等前面全部的線程都數據處理完成(每一個線程處理的時間各不相同,結束的時間各不相同),這個線程 纔開始進行處理數據。 爲了處理這個解決這個線程的問題,Java語言爲咱們提供了集中語言上面的機制來解決,其中有CountDownLatch類、join函數、CylicBarrier類等…… 下面我將會對CountDownLatch類和join函數這兩種就最具備典型對比意義的機制進行講解與說明的使用方法、聯繫與區別,CylicBarrier類的使用會在後續給出。java
CountDownLatch類使用 CountDownLatch類,一個同步輔助類,在完成一組正在其餘線程中執行的操做以前,它容許一個或多個線程一直等待。併發
類中主要方 法dom
public CountDownLatch(int count);--- 減小多少個計數 public void countDown();-- -減小1個計數 public void await() throws InterruptedException---一直等待,直到計數器被減到0位置,後面 的線程才能執行 public void await(timeout,unit)throws InterruptedException---等待時間(時間數目、時間單位 時|分|秒)
方法介紹完了,直接上代碼了。函數
import java.util.Random; import java.util.concurrent.CountDownLatch; public class CountDownLatchMain { public static final int THREAD_NUMB = 3;//線程個數 public static void main(String[] args) throws Throwable { CountDownLatch l = new CountDownLatch(THREAD_NUMB); for (int i = 0; i < THREAD_NUMB; i++) { new Thread(new CountDownLatchTask(l, String.valueOf(i))). start (); } l.await();//主線程等待,直到全部的線程執行完成,後面的才進行執行 System.out.println("全部線程任務都結束了,後面的任務纔開始……"); Thread.sleep(new Random().nextInt(10) * 1000); System.out.println("任務結束了"); } } class CountDownLatchTask implements Runnable { private CountDownLatch latch; private String threadname; public CountDownLatchTask(CountDownLatch latch, String threadname) { this.latch = latch; this.threadname = threadname; } public void run() { try { System.out.println("線程" + this.threadname + "任務開始……"); Thread.sleep(new Random().nextInt(3) * 1000); System.out.println("線程" + this.threadname + "任務結束……"); } catch (InterruptedException e) { e.printStackTrace(); } finally { if (latch != null) { latch.countDown();//內部計算器減一 } } } }
join函數使用this
在java線程Thread類中的join函數一樣具備和CountDownLatch類一樣的效果來實現上述代碼的功能。廢話很少說,直接上代碼。兩個代碼很是類似,驚呆了~線程
import java.util.ArrayList; import java.util.List; import java.util.Random; public class CountDownLatchMain { public static final int THREAD_NUMB = 3; public static void main(String[] args) throws Throwable { List<Thread> tList = new ArrayList<>(); for (int i = 0; i < THREAD_NUMB; i++) { Thread t = new Thread(new CountDownLatchTask(String.valueOf(i))); t.start(); tList.add(t); } for (Thread t : tList) { t.join();//這個等待其餘線程執行完成後,而後在執行本線程後的任務 } System.out.println("全部線程任務都結束了,後面的任務纔開始……"); Thread.sleep(new Random().nextInt(10) * 1000); System.out.println("任務結束了"); } } class CountDownLatchTask implements Runnable { private String threadname; public CountDownLatchTask(String threadname) { super(); this.threadname = threadname; } public void run() { try { System.out.println("線程" + this.threadname + "任務開始……"); Thread.sleep(new Random().nextInt(10) * 1000); System.out.println("線程" + this.threadname + "任務結束……"); } catch (InterruptedException e) { e.printStackTrace(); } } }
CountDownLatch類與join函數的聯繫與區別code
前面的部分談了他們使用,下面來談他們間的聯繫與區別.對象
聯繫:同步
他們都可以實現本文在前言中提到的場景,而且在前面使用的時候,代碼也體現出來了。it
區別:
場景一
在下面這個場景中CountDownLatch類比join函數實現更好: 例如:前面多個併發的線程中的任務分爲兩個階段,每個線程在第一個階段的任務執行完成後,彙總的線程就只須要對前面全部線程第一階段的數據進行處理,而並不關心前面併發線程的第二階段的任務,在當前場景下join函數必需要等待前面全部的線程都執行完了後面的線程,才能執行,而CountDownLatch類實現效果更好。 代碼以下
import java.util.Random; import java.util.concurrent.CountDownLatch; public class CountDownLatchMain { public static final int THREAD_NUMB = 3;// 線程個數 public static void main(String[] args) throws Throwable { CountDownLatch l = new CountDownLatch(THREAD_NUMB); for (int i = 0; i < THREAD_NUMB; i++) { new Thread(new CountDownLatchTask(l, String.valueOf(i))).start(); } l.await();// 主線程等待,直到全部的線程執行完成,後面的才進行執行 System.out.println("全部線程 1階段 任務都結束了,後面的任務纔開始……"); Thread.sleep(new Random().nextInt(10) * 1000); System.out.println("任務結束了"); } } class CountDownLatchTask implements Runnable { private CountDownLatch latch; private String threadname; public CountDownLatchTask(CountDownLatch latch, String threadname) { this.latch = latch; this.threadname = threadname; } public void run() { try { System.out.println("線程" + this.threadname + " 1階段---任務開始……"); Thread.sleep(new Random().nextInt(3) * 1000); System.out.println("線程" + this.threadname + " 1階段---任務結束……"); latch.countDown();// 內部計算器減一 System.out.println("線程" + this.threadname + " 2階段---任務開始……"); Thread.sleep(new Random().nextInt(20) * 1000); System.out.println("線程" + this.threadname + " 2階段---任務結束……"); } catch (InterruptedException e) { e.printStackTrace(); } } }
場景二
當前面執行的併發任務在線程池中執行時,想要作到前面全部的併發任務都完成後,而後再執行後面的操做,join函數就顯得無能爲力,而CountDownLatch類則能夠解決問題。 上代碼:
import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; public class CountDownLatchMain { public static final int THREAD_NUMB = 3;// 線程個數 public static void main(String[] args) throws Throwable { CountDownLatch l = new CountDownLatch(THREAD_NUMB); Executor executor = Executors.newFixedThreadPool(4);// 固定線程池 for (int i = 0; i < THREAD_NUMB; i++) { executor.execute(new CountDownLatchTask(l, String.valueOf(i))); } l.await();// 主線程等待,直到全部的線程執行完成,後面的才進行執行 System.out.println("全部線程任務都結束了,後面的任務纔開始……"); Thread.sleep(new Random().nextInt(10) * 1000); System.out.println("任務結束了"); } } class CountDownLatchTask implements Runnable { private CountDownLatch latch; private String threadname; public CountDownLatchTask(CountDownLatch latch, String threadname) { this.latch = latch; this.threadname = threadname; } public void run() { try { System.out.println("線程" + this.threadname + "任務開始……"); Thread.sleep(new Random().nextInt(3) * 1000); System.out.println("線程" + this.threadname + "任務結束……"); latch.countDown();// 內部計算器減一 } catch (InterruptedException e) { e.printStackTrace(); } } }
場景三
CountDownLatch對象中的countDown()函數能夠傳入參數,讓內部計數器提早到達0,這樣後面等待的線程能夠提早執行,而沒必要等待剩下的線程執行完成才執行。 latch.countDown(3);// 內部計算器減三 這種場景有存在的可能,看需求……
總結 CountDownLatch類、join函數兩種機制,都能起到一個或幾個線程等待前面併發的線程執行完成後,在執行的功能,可是有時候在特殊場景下CountDownLatch類會有更好的用。