1. 程式人生 > >CyclicBarrier和CountDownLatch的區別

CyclicBarrier和CountDownLatch的區別

起點 exceptio blog read cep 復雜 util mil nds

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 = new
CountDownLatch(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的區別