CyclicBarrier和CountDownLatch的區別
CountDownLatch
Countdownlatch是一個同步工具類;用來協調多個線程之間的同步;
這個工具通常用來控制線程等待;它可以讓某一個線程等待知道倒計時結束,在開始執行;
CountDownLatch的兩種用法:
1. 某一線程在開始運行前等待n個線程執行完畢;將CountDownLatch的計數器初始化為n:new CountDownLatch(n);每當一個任務線程執行完畢,就將計數器減1,CountDownLatch.Countdown;當計數器的值變為0時;在CountDownLatch上await()的線程就會被喚醒。一個典型應用場景就是啟動一個服務時,主線程需要等待多個組件加載完畢;
2. 實現多個線程開始執行任務的最大並行性;註意是並行性;而不是並發性;強調的是多個線程在某一時刻同時執行,類似於賽跑;將多個線程放到起點;等待發令槍響;然後同時開跑;做法是初始化一個共享的CountDownLatch對象;將其計數器初始化為1:new CountdownLatch(1);多個線程在開始執行任務前首先CountDownLatch.await(),當主線程調用countdownl時,計數器變為0;多個線程同時被喚醒;
package com.practice.test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; public class CountDownLatchExample1 { public static final int threadCount = 550; public static void main(String[] args) throws InterruptedException { ExecutorService threadpool = Executors.newFixedThreadPool(300); final CountDownLatch cl = newCountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { final int threadnum = i; threadpool.execute(() -> { try { test(threadnum); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { cl.countDown(); } }); } cl.await(); threadpool.shutdown(); System.out.println("finish"); } public static void test(int threadnum) throws InterruptedException { Thread.sleep(1000); System.out.println("threadnum" + threadnum); Thread.sleep(1000); } }
3. Countdownlatch的不足
Countdownlatch是一次性的,計數器的值只能在構造方法中初始化一次,之後沒有任何機制再次對其設置值,當CountDownLatch使用完畢後,他不能再次被使用
CyclicBarrier
CyclicBarrier和CountDownLatch非常類似,他也可以實現線程間的技術等待,但是它的功能比CountDownLatch更加強大和復雜。主要應用場景和CountDownLatch類似
CyclicBarrier的字面意思是可循環使用的屏障,它要做的事情是,讓一組線程到達一個屏障時被阻塞,直到最後一個線程到達屏障時,屏障才會開門,所有被屏障攔截的線程才會繼續幹活。CyclicBarrier默認的構造方法時CyclicBarrier(int parties),其參數表示屏障攔截的線程數量,每個線程調用await方法告訴CyclicBarrier我已經到達了屏障。然後當前線程被阻塞;
package com.practice.test; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class CyclicBarrierExample2 { private static final int threadcount = 550; private static final CyclicBarrier cyb = new CyclicBarrier(5); public static void main(String[] args) throws InterruptedException { ExecutorService threadpool = Executors.newFixedThreadPool(10); for (int i = 0; i < threadcount; i++) { final int num = i; Thread.sleep(1000); threadpool.execute(() -> { try { try { test(num); } catch (TimeoutException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } }); } threadpool.shutdown(); } public static void test(int threadnum) throws InterruptedException, BrokenBarrierException, TimeoutException { System.out.println("threadnum" + threadnum + "is ready"); try { cyb.await(2000, TimeUnit.MILLISECONDS); } catch (Exception e) { System.out.println("CyclicBarrier"); } System.out.println("threadnum" + threadnum + "isFinish"); } }
CyclicBarrier的應用場景
CyclicBarrier可以用於多線程計算數據,最後合並結果的應用場景。比如我們用一個Excel保存了用戶所有銀行流水,每個Sheet保存每一個賬戶近一年的每一筆銀行流水,現在需要統計用戶的日均銀行流水,先用多線程處理每個sheet裏的銀行流水,都執行完之後,得到每個sheet的日均銀行流水,最後,使用barrierAction用這些線程的計算結果,計算出整個Excel的日均銀行流水;
CyclicBarrier(int parties,Runnable BarrierAction),在線程到達屏障時,優先執行BarrierAction,方便處理更復雜的業務場景;
package com.practice.test; import java.util.concurrent.CyclicBarrier; public class CyclicBarrierTest { public static void main(String[] args) { CyclicBarrier cyl = new CyclicBarrier(5, () -> { System.out.println("線程組執行結束"); }); for (int i = 0; i < 5; i++) { new Thread(new Readnum(i, cyl)).start(); } } static class Readnum implements Runnable { private int id; private CyclicBarrier cdl; public Readnum(int id, CyclicBarrier cdl) { super(); this.id = id; this.cdl = cdl; } @Override public void run() { synchronized (this) { System.out.println(Thread.currentThread().getName() + "id"); try { cdl.await(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + id + "結束了"); } } } }
CyclicBarrier和CountDownLatch的區別
CountDownLatch是計數器,只能使用一次,而CyclicBarrier的計數器提供reset功能,可以多次使用,
JavaDoc中的定義:
CountDownLatch:一個或者多個線程,等待其他線程完成某件事情之後,等待其他多個線程完成某件事情之後才能執行;
CyclicBarrier:多個線程互相等待,直到到達同一個同步點,再繼續一起執行;
對於CountDownLatch來說,重點是一個線程(多個線程)等待”,而其他的N個線程在完成”某件事情“之後,可以終止,也可以等待,而對於CyclicBarrier,重點是多個線程,在任意一個線程沒有完成,所有的線程都必須等待;
CountDownLatch是計數器,線程完成一個記錄一個,只不過計數不是遞增而是遞減,而CyclicBarrier更像是一個閥門,需要所有線程都要到達,閥門才能打開,然後繼續執行;
文章參考自:
https://github.com/Snailclimb
https://blog.csdn.net/tolcf/article/details/50925145?utm_source=blogxgwz0
CyclicBarrier和CountDownLatch的區別