Java学习笔记-并发包-CyclicBarrier

本文介绍了Java并发包中的CyclicBarrier工具类,通过代码示例展示了如何使用CyclicBarrier让多个线程相互等待,以及它的构造方法和核心方法await()。CyclicBarrier允许设置固定数量的线程在屏障处等待,当所有线程到达后执行指定任务,且屏障可复用。源码解析部分详细解释了CyclicBarrier的工作原理。
摘要由CSDN通过智能技术生成

一、代码示例

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class CyclicBarrierDemo {

    public static void main(String[] args) throws Exception{
		// 创建一个CyclicBarrier 
        CyclicBarrier cyclicBarrier = new CyclicBarrier(4,() -> System.out.println("----------开始检票-------"));

        ExecutorService executorService = Executors.newFixedThreadPool(3);
		// 开始执行
        executorService.execute(new TicketsCheckTask(cyclicBarrier, "张一", 7));
        executorService.execute(new TicketsCheckTask(cyclicBarrier, "张二", 3));
        executorService.execute(new TicketsCheckTask(cyclicBarrier, "张三", 6));
		// 将主线程也加入到CyclicBarrier中,达到和CountDownLatch一样的效果
        cyclicBarrier.await();
        executorService.shutdown();
        System.out.println("全部已上车");
    }



    static class TicketsCheckTask implements Runnable {

        private final CyclicBarrier cyclicBarrier ;

        private final String name;

        private final int sleepTime;

        public TicketsCheckTask(CyclicBarrier cyclicBarrier, String name, int sleepTime) {
            this.cyclicBarrier = cyclicBarrier;
            this.name = name;
            this.sleepTime = sleepTime;
        }

        @Override
        public void run() {
           try {
				// 休眠一段时间
               TimeUnit.SECONDS.sleep(sleepTime);
               System.out.println("开始候车,到了:"+name);
               // 屏障等待
               cyclicBarrier.await();
           }catch (Exception e){
               e.printStackTrace();
           }
        }
    }

二、源码解析

1. CyclicBarrier 类的以下方法和变量

1

2. 构造方法
	// 参数 parties 就是设置n个线程到达屏障之后会打破屏障
	public CyclicBarrier(int parties) {
        this(parties, null);
    }
    // parties 同上,barrierAction 指打破屏障之后会执行动作,就是上面例子一样,等待候车人到齐之后触发开始检票的动作
	public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }    
3. 主要方法 await()、dowait()
	//没有参数
    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }
    // 带有超时时间
    public int await(long timeout, TimeUnit unit)
            throws InterruptedException,
            BrokenBarrierException,
            TimeoutException {
        return dowait(true, unit.toNanos(timeout));
    }

这两个方法都是调用了dowait方法,所以我们看下dowait方法干了什么。

    private int dowait(boolean timed, long nanos)
           throws InterruptedException, BrokenBarrierException,
           TimeoutException {
       //拿到可重入锁 new的时候创建好
       final ReentrantLock lock = this.lock;
       //加锁 保证线程安全
       lock.lock();
       try {
           // 这一次的屏障是否打破,初始化的时候为false,breakBarrier()之后设置为true
           final Generation g = generation;
           // 如果true 则抛异常
           if (g.broken)
               throw new BrokenBarrierException();
           // 判断当前线程是否中断,如果中断则 1. 设置屏障已破损,并唤醒其他线程 2. 抛出异常
           if (Thread.interrupted()) {
               breakBarrier();
               throw new InterruptedException();
           }
           // count 总个数减1,代表此批次有一个线程到达了屏障
           int index = --count;
           // 如果index=0 则代表他是此批次最后一个到达屏障的线程
           if (index == 0) {  // tripped
               boolean ranAction = false;
               try {
                   // 如果屏障到达所需执行的runnable 不是null 则执行。并唤醒其他线程
                   final Runnable command = barrierCommand;
                   if (command != null)
                       command.run();
                   ranAction = true;
                   // 这个方法会唤醒其他线程,并重新设置generation
                   nextGeneration();
                   return 0;
               } finally {
                   // 如果执行command的时候出现异常也要保证打破屏障唤醒其他线程
                   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 {
                       // We're about to finish waiting even if we had not
                       // been interrupted, so this interrupt is deemed to
                       // "belong" to subsequent execution.
                       Thread.currentThread().interrupt();
                   }
               }

               if (g.broken)
                   throw new BrokenBarrierException();
               // 执行到这,代表1. 有线程唤醒它 2. 超时时间到了 自动唤醒了
               //  g 不等于 generation 就可以返回index。这是因为如果最后一个线程到达屏障之后,肯定会重新设置generation,所以
               // 不等于就代表此批次的线程都已经到达屏障了。
               if (g != generation)
                   return index;
               // 能执行到,代表超时时间到了,然而其他线程还没全部到达屏障,则打破屏障,抛出异常
               if (timed && nanos <= 0L) {
                   breakBarrier();
                   throw new TimeoutException();
               }
           }
       } finally {
           // 解锁
           lock.unlock();
       }
   }
4. 总结
  • CyclicBarrier 就是一种可以设置固定数量的线程,让线程间相互等待到达同一个屏障的工具。并且可以设置runnable使其到达之后完成自定义的一些动作。而且这个屏障是可以循环复用的。
  • 它和CountDownLatch的相同点是可以让固定数目的线程相互等待,不同点是CountDownLatch不可以复用,不可以设置到达之后的runnable。实现原理也不同,CyclicBarrier 直接使用ReentrantLock,Condition实现,CountDownLatch使用AQS实现。

努力学习,加油!!!!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值