【JUC】CyclicBarrier的瞭解和使用

概念

權威指南

  • 一種同步輔助工具,它允許一組線程全部互相等待以到達一個公共的障礙點。CyclicBarriers在涉及固定大小的線程方的程序中很有用,該線程方有時必須互相等待。屏障被稱爲循環屏障, 因爲它可以在釋放等待線程之後重新使用。
  • CyclicBarrier支持可選的Runnable命令,該命令在聚會的最後一個線程到達之後但在釋放任何線程之前,每個障礙點運行一次。此屏障操作對於在任何一方繼續之前更新共享狀態很有用。

個人理解

  • 給每個線程設置一個閾值,當每個線程逗達到這個閾值的時候纔會去執行。
  • 再抽象一點的話,我們去遊樂園做船,這個船坐滿的時候纔會開出去。不坐滿的話來的早的人就得等待來的晚的的人。所以這些人也就是會互相等待的。當人坐滿的時候纔會出發去島上。這個船還可以重複被使用,當這批人被送走後,還可以承載下一批人。也就是CyclicBarrier的重複使用。

源碼

常用方法和構造方法

  • 核心源碼
  1. 在CyclicBarrier初始化時,設置柵欄數(new CyclicBarrier( int parties)),每有一個線程執行await方法(用Condition的await阻塞),柵欄就減少一個,直到減少到0,取消等待,喚醒線程(用Condition的signalAll喚醒)
  2. 任何一個線程被interrupt打斷,Condition也會調用signalAll方法喚醒所有阻塞的線程。
  3. 有reset方法可以還原。(這也是和counDownLatch的最大的區別之一)
  4. 大概介紹:詳細介紹的文章:點擊此處
 /**
     * Main barrier code, covering the various policies.
     */
    private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            final Generation g = generation;

            if (g.broken)
                throw new BrokenBarrierException();
            …………………………………………
        }

SHOW ME CODE

package JUC;

import lombok.SneakyThrows;

import java.awt.*;
import java.util.concurrent.CyclicBarrier;

/**
 * @authoryuanxindong
 * @date: 2020/6/18 11:53 下午
 */
public class CyclicBarrierDemo implements Runnable{
    private int  timeOut ;
    private CyclicBarrier cyclicBarrier;

    public void setTimeOut(int timeOut) {
        this.timeOut = timeOut;

    }
    public  static class innerCyclicBarrierDemo  implements Runnable{
        @Override
        public void run() {
            System.out.println("最後執行的方法+");
        }
    }

    public CyclicBarrierDemo(int timeOut,CyclicBarrier cyclicBarrier) {
        this.timeOut = timeOut;
        this.cyclicBarrier = cyclicBarrier;
    }


    public static void main(String[] args) {
        // 啓用一個CyclicBarrier,然後進行加載。
        CyclicBarrier cyclicBarrier =new CyclicBarrier(3,new innerCyclicBarrierDemo());
        long startTime = System.currentTimeMillis();
        CyclicBarrierDemo cyclicBarrierDemo = new CyclicBarrierDemo(1000,cyclicBarrier);
        Thread thread = new Thread(cyclicBarrierDemo);
        CyclicBarrierDemo cyclicBarrierDemo2 = new CyclicBarrierDemo(2000,cyclicBarrier);
        Thread thread1 = new Thread(cyclicBarrierDemo2);
        CyclicBarrierDemo cyclicBarrierDemo3 = new CyclicBarrierDemo(3000,cyclicBarrier);
        Thread thread2 = new Thread(cyclicBarrierDemo3);
           thread.start();
           thread1.start();
           thread2.start();


        System.out.println("所有線程執行結束執行時間:"+(System.currentTimeMillis() -startTime));
    }
    @SneakyThrows
    @Override
    public void run() {
        System.out.println("開始執行:「」"+Thread.currentThread().getName()+System.currentTimeMillis());
        Thread.sleep(timeOut);
      long watingTime = System.currentTimeMillis();
        System.out.println("等待:「」"+Thread.currentThread().getName()+watingTime);
        cyclicBarrier.await();
        System.out.println("完成:「」"+Thread.currentThread().getName() +"    time:    "+ (System.currentTimeMillis()-watingTime));
    }

}

執行結果

開始執行:「」Thread-01592752948830
開始執行:「」Thread-11592752948830
所有線程執行結束執行時間:2
開始執行:「」Thread-21592752948831
等待:「」Thread-01592752949833
等待:「」Thread-11592752950835
等待:「」Thread-21592752951836
最後執行的方法+
完成:「」Thread-2    time:    0
完成:「」Thread-0    time:    2003
完成:「」Thread-1    time:    1001
Disconnected from the target VM, address: '127.0.0.1:58105', transport: 'socket'

Process finished with exit code 0
  • 我們細細的品一品:爲什麼線程2等待的時間最短呢?
    因爲他是做後一個執行完的,他不用等待別人而是執行快的在等待他。

Code analysis

  • 代碼中我們分別給了3個線程,每個線程分別讓執行不同的時間,然後通過調用CyclicBarrier的await方法,進行欄柵,第一個執行完後第一個進到欄柵狀態所以等待第二個到達的時候第一個線已經等待了大約1000ms了,但是 第三個在此刻執行了2000MS,還有1000ms沒有執行,所以還需等待第三個線程池,所以這個過程,第一個線程等待後兩個線程大概用了2000MS 第二個等待隨後一個線程大概用了1000s,只有第三個線程沒有等待時間,在線程狀態切換和執行的時候浪費了1MS。
  • 從上面我們也可以看到我們將一個RunAble初始化後,他會在最後一次線程等待結束後立馬執行。並且是在線程本身的任務之前執行的。
  • 通過上面的分析,我們也證明了CyclicBarrier的功能。

總結

  1. CyclicBarrierd可以讓多個線程之間相互等待
  2. 也可以,設置一個當所有線程都達到欄柵時再執行一個線程。(上面的例子,當船上滿人的時候,這個時候船就會開動,這個開動就是Runable要執行的任務)
  3. CyclicBarrierd的欄柵是可以循環使用的,所以也叫回環欄柵。

參考資料

  1. https://blog.csdn.net/qq_17305249/article/details/78081002 (底層分析)
  2. https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CyclicBarrier.html(基本使用)
  3. https://blog.csdn.net/HalfImmortal/article/details/106796659(重點推薦)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章