閉鎖CountDownLatch和柵欄CyclicBarrier之異同舉例

CountDownLatch和CyclicBarrier的主要聯繫和區別以下:
1.閉鎖CountDownLatch作減計數,而柵欄CyclicBarrier則是加計數。
2.CountDownLatch是一次性的,CyclicBarrier能夠重用。
3.CountDownLatch強調一個線程等多個線程完成某件事情。CyclicBarrier是多個線程互等,等你們都完成。
4.鑑於上面的描述,CyclicBarrier在一些場景中能夠替代CountDownLatch實現相似的功能。

另外,值得一提的是,CountDownLatch和CyclicBarrier在建立和啓動線程時,都沒有明確提到同時啓動所有線程,事實上這在技術上是不大可能,沒必要要,不提倡的。

先看例子一:
java

class SubRunnable implements Runnable {
    private CountDownLatch begin, end;
    private List<Integer> sublist;
    
    public SubRunnable(List<Integer> sublist, CountDownLatch begin,CountDownLatch end) {
        this.sublist = sublist;
        
        this.begin = begin;
        this.end = end;
    }
    
    @Override
    public void run() {
        try {
            begin.await();            
            
            if (sublist != null) {
                for (int i : sublist) {
                    System.out.println("線程" + Thread.currentThread().getName() + ", i = " + i);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally{
            System.out.println(System.currentTimeMillis() + ",線程" + Thread.currentThread().getName() + ",開始執行!");
            end.countDown();
        }
    }
}
public class BatchWithCountDownLatch {
    private static final int MAX = 3;
    
    private static void list(List<Integer> list) {
        if(list == null){
            list = new ArrayList<Integer>();
        }
        
        for(int i = 0 ;i < 1000;i++){
            list.add(i);
        }
    }
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();
        list(list);
        
        //把list拆分紅多個
        int mod = list.size() % MAX;
        int threadCount = mod == 0 ? list.size() / MAX : list.size() / MAX + 1;
        ExecutorService executors = Executors.newFixedThreadPool(threadCount);
        
        CountDownLatch begin = new CountDownLatch(1); 
        CountDownLatch end = new CountDownLatch(threadCount); 
                
        for(int i = 0; i< threadCount;i++){
            int subsize = (i + 1) * MAX;
            executors.execute(new SubRunnable(list.subList(i * MAX, subsize > list.size() ? list.size() : subsize),begin,end));
        }
        
        System.out.println("開始 !");
        begin.countDown();
        long startTime = System.currentTimeMillis();
        
        try {
            end.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("線程" + Thread.currentThread().getName() + "," + System.currentTimeMillis() + ", 全部線程已完成,開始進入下一步!");
            System.out.println("花費時間 -> " + (System.currentTimeMillis() - startTime) + " ms");
        }
        
        System.out.println("開始進入第二步操做! ");        
        
        System.out.println("end! ");
    }
}

這是根據jdk文檔中的僞代碼例程,編寫的一個例子,咱們徹底能夠將這個例程改成只使用一個CountDownLatch來實現之。通過測試,發現begin的引入對程序基本無用,當list是1000的數量級時,最早啓動的線程仍然比最後啓動的快幾十毫秒左右;而不設置begin開始閉鎖的程序,也是徹底同樣的狀況。

例子二:
ide

class SubRunnable implements Runnable {
    private CyclicBarrier cyclicBarrier;
    private List<Integer> sublist;
    
    public SubRunnable(List<Integer> sublist, CyclicBarrier cyclicBarrier) {
        this.sublist = sublist;
        this.cyclicBarrier = cyclicBarrier;
    }
    
    @Override
    public void run() {        
        try {
            System.out.println(System.currentTimeMillis() + ",線程" + Thread.currentThread().getName() + ",開始執行!");
            if(sublist != null){
                for(int i : sublist){
                    System.out.println("線程" + Thread.currentThread().getName() + ", i = " + i);
                }
            }
            cyclicBarrier.await();
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
    }    
}
public class ReplaceCountDownLatch {
    private static final int MAX = 3;
    
    private static void list(List<Integer> list) {
        if(list == null){
            list = new ArrayList<Integer>();
        }
        
        for(int i = 0 ;i < 10;i++){
            list.add(i);
        }
    }
    
    public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
        List<Integer> list = new ArrayList<Integer>();
        list(list);
        //把list拆分紅多個
        int mod = list.size() % MAX;
        int threadCount = mod == 0 ? list.size() / MAX : list.size() / MAX + 1;
        ExecutorService executors = Executors.newFixedThreadPool(threadCount);
        final CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount,new Runnable() {
            @Override
            public void run() {
                //根據jdkdoc裏的描述,哪一個線程最後運行完,就執行下面的代碼。
                System.out.println("線程" + Thread.currentThread().getName() + "," + System.currentTimeMillis() + ", 全部線程已完成,開始進入下一步!");
            }
        });
        
        for(int i = 0; i< threadCount;i++){
            int subsize = (i + 1) * MAX;
            executors.execute(new SubRunnable(list.subList(i * MAX, subsize > list.size() ? list.size() : subsize),cyclicBarrier));
        }
        
        cyclicBarrier.await();
        executors.shutdown();
        System.out.println("開始進入第二步操做! ");
        
        
        System.out.println("end! ");
    }
}

使用柵欄CyclicBarrier實現和上面閉鎖CountDownLatch相同的功能。

測試

相關文章
相關標籤/搜索