CyclicBarrier实现原理核心逻辑

CyclicBarrier作用:
CyclicBarrier用于同步一组线程,创建CyclicBarrier时指定参与循环栅栏的线程数量与一组线程全部就位时,优先执行的动作。 每个线程调用await()方法后,即表示到达栅栏点,然后就会进阻塞,当参与循环栅栏的最后一个线程调用了await()方法后,会唤醒之前阻塞的线程,循环栅栏重置,进入下一次循环。

CyclicBarrier字段解释:

	//	表示本次循环的状态:为true时,表示本次循环被破坏(如,有线程被中断、await超时)
	private static class Generation {
    
    
        boolean broken = false;
    }

    /** 在执行await时是独占的 */
    private final ReentrantLock lock = new ReentrantLock();
    /** 用于多个线程之间的同步 */
    private final Condition trip = lock.newCondition();
    /** 参与一次循环的线程数 */
    private final int parties;
    /* 所有都达到循环栅栏点时,优先执行的动作 */
    private final Runnable barrierCommand;
    /** The current generation */
    private Generation generation = new Generation();
   	/** 用来表示当前剩余的需要到达栅栏点的线程数量  **/
    private int count;

核心逻辑实现:

CyclicBarrier核心逻辑的实现: dowait(boolean timed, long nanos)。
当调用await方法后,最后会调用 dowait(boolean timed, long nanos)。

执行流程:
首次明确这个过程需要加锁,因为会涉及到多个共享变量的赋值与线程之间的同步。

拿到锁后,先检查本次循环的状态是否被破坏,若被破坏则直接抛出异常。
然后检查当前线程是否被中断,若被中断调用breakBarrier()去终止本次循环:将循环状态置为无效状态,将剩余需要到达栅栏点的线程数置为初始值,最后唤醒所以调用await进入阻塞状态的线程。breakBarrier()执行完毕后抛出中断异常。

检查通过的话,将需要进入栅栏点的线程数减一(–count)
减完后的值若为0:代表本次循环执行完毕,此时若创建CyclicBarrier时指定了到达栅栏点需要执行的任务,则先执行该任务,然后调用nextGeneration()去更新状态,为下一次循环初始化: 先唤醒所有等待的线程,将count设置为初始值parties,新建一个Generation赋值给generation变量。
如果–count后的值不为0,则代表还有线程未准备就绪,则进入阻塞过程,阻塞过程发生中断调用breakBarrier()。当从阻塞状态被唤醒后,会检查当前代(generation)是否被破坏,破坏直接抛出栅栏被破坏。未被破坏,检查是否正常换代,即检查进入await方法时拿到的 generation的引用(g)与现在的generation还是否是同一个?在–count为0时,执行了nextGeneration()方法,该方法会新建一个Generation赋值给generation变量。所以在这里如果g!=generation代表成功换代。

参照源码理解上述过程:

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();
 
        // 如果线程中断了,抛出异常
        if (Thread.interrupted()) {
    
    
            // 将损坏状态设置为true
            // 并通知其他阻塞在此栅栏上的线程
            breakBarrier();
            throw new InterruptedException();
        }
 
        // 获取下标
        int index = --count;
        // 如果是 0,说明最后一个线程调用了该方法
        if (index == 0) {
    
      // tripped
            boolean ranAction = false;
            try {
    
    
                final Runnable command = barrierCommand;
                // 执行栅栏任务
                if (command != null)
                    command.run();
                ranAction = true;
                // 更新一代,将count重置,将generation重置
                // 唤醒之前等待的线程
                nextGeneration();
                return 0;
            } finally {
    
    
                // 如果执行栅栏任务的时候失败了,就将损坏状态设置为true
                if (!ranAction)
                    breakBarrier();
            }
        }
 
        // loop until tripped, broken, interrupted, or timed out
        for (;;) {
    
    
            try {
    
    
                 // 如果没有时间限制,则直接等待,直到被唤醒
                if (!timed)
                    trip.await();
                // 如果有时间限制,则等待指定时间
                else if (nanos > 0L)
                    nanos = trip.awaitNanos(nanos);
            } catch (InterruptedException ie) {
    
    
                // 当前代没有损坏
                if (g == generation && ! g.broken) {
    
    
                    // 让栅栏失效
                    breakBarrier();
                    throw ie;
                } else {
    
    
                    // 上面条件不满足,说明这个线程不是这代的
                    // 就不会影响当前这代栅栏的执行,所以,就打个中断标记
                    Thread.currentThread().interrupt();
                }
            }
 
            // 当有任何一个线程中断了,就会调用breakBarrier方法
            // 就会唤醒其他的线程,其他线程醒来后,也要抛出异常
            if (g.broken)
                throw new BrokenBarrierException();
 
            // g != generation表示正常换代了,返回当前线程所在栅栏的下标
            // 如果 g == generation,说明还没有换代,那为什么会醒了?
            // 因为一个线程可以使用多个栅栏,当别的栅栏唤醒了这个线程,就会走到这里,所以需要判断是否是当前代。
            // 正是因为这个原因,才需要generation来保证正确。
            if (g != generation)
                return index;
            
            // 如果有时间限制,且时间小于等于0,销毁栅栏并抛出异常
            if (timed && nanos <= 0L) {
    
    
                breakBarrier();
                throw new TimeoutException();
            }
        }
    } finally {
    
    
        // 释放独占锁
        lock.unlock();
    }
}

让当前栅栏失效:

private void breakBarrier() {
    
    
        generation.broken = true;
        count = parties;
        trip.signalAll();
    }

进行换代:

	private void nextGeneration() {
    
    
        // signal completion of last generation
        trip.signalAll();
        // set up next generation
        count = parties;
        generation = new Generation();
    }

猜你喜欢

转载自blog.csdn.net/qq_40728028/article/details/106469487