• Java并发编程之CyclicBarrier


    简介

    CyclicBarrier字面意思是循环屏障,它可以实现线程间的计数等待。当线程到达屏障点时会依次进入等待状态,直到最后一个线程进入屏障点时会唤醒等待的线程继续运行

    CyclicBarrier和CountDownLatch类似,区别在于CountDownLatch只能使用一次,当计数器归零后,CountDownLatch的await等方法都会直接返回。而CyclicBarrier是可以重复使用的,当计数器归零后,计数器和CyclicBarrier状态都会被重置。

    CyclicBarrier的使用

    构造方法介绍

    CyclicBarrier(int parties):创建CyclicBarrier,指定计数器值(等待线程数量)。

    CyclicBarrier(int parties, Runnable barrierAction):创建CyclicBarrier,指定计数器值(等待线程数量)和计数器归零后(最后一个线程到达)要执行的任务。

    核心方法介绍

    await():阻塞当前线程,直到计数器归零被唤醒或者线程被中断。

    await(long timeout, TimeUnit unit):阻塞当前线程,直到计数器归零被唤醒、线程被中断或者超时返回。

    CyclicBarrier例子

    等待所有玩家准备就绪,游戏才开始,每一轮游戏的开始意味着CyclicBarrier已经重置,可以开始新一轮的计数。

    public class Demo {
        public static void main(String[] args) {
            //创建CyclicBarrier并指定计数器值为5,以及计数器为0后要执行的任务
            CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {
                System.out.println("---游戏开始---");
                System.out.println("---五票赞成,游戏结束---");
            });
    
            Runnable runnable = () -> {
                //重复使用CyclicBarrier5次
                for(int i = 0; i < 5; i++){
                    System.out.println(Thread.currentThread().getName() + ":准备就绪");
                    try {
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            };
    
            Thread thread1 = new Thread(runnable, "一号玩家");
            Thread thread2 = new Thread(runnable, "二号玩家");
            Thread thread3 = new Thread(runnable, "三号玩家");
            Thread thread4 = new Thread(runnable, "四号玩家");
            Thread thread5 = new Thread(runnable, "五号玩家");
            thread1.start();
            thread2.start();
            thread3.start();
            thread4.start();
            thread5.start();
        }
    }
    
    /*
     * 循环输出5次
     * 输出结果:
     * 一号玩家:准备就绪
     * 三号玩家:准备就绪
     * 二号玩家:准备就绪
     * 五号玩家:准备就绪
     * 四号玩家:准备就绪
     * ---游戏开始---
     * ---五票赞成,游戏结束---
     * 三号玩家:准备就绪
     * 一号玩家:准备就绪
     * 五号玩家:准备就绪
     * ......
     */
    
    破损的CyclicBarrier

    在使用CyclicBarrier中,假设总的等待线程数量为5,现在其中一个线程被中断了,被中断的线程将抛出InterruptedException异常,而其他4个线程将抛出BrokenBarrierException异常。

    BrokenBarrierException异常表示当前的CyclicBarrier已经破损,可能不能等待所有线程到齐了,避免其他线程永久的等待。

    CyclicBarrier的源码

    CyclicBarrier是基于显式锁ReentrantLock来实现的,CyclicBarrier很多方法都使用显式锁做了同步处理,await方法的等待唤醒也是通过Condition实现的。

    CyclicBarrier的成员变量:

    //显式锁
    private final ReentrantLock lock = new ReentrantLock();
    //用于显式锁的Condition
    private final Condition trip = lock.newCondition();
    //线程数量
    private final int parties;
    //当所有线程到达屏障点后执行的任务
    private final Runnable barrierCommand;
    //Generation内部有一个broken变量,用于标识CyclicBarrier是否破损
    private Generation generation = new Generation();
    //用于递减的线程数量,在每一轮结束后会被重置为parties
    private int count;
    

    await方法里是调用的dowait方法,dowait方法源码:

    private int dowait(boolean timed, long nanos)throws InterruptedException, BrokenBarrierException,TimeoutException {
    	final ReentrantLock lock = this.lock;
    	lock.lock();
    	try {
    		final Generation g = generation;
    		
    		//如果CyclicBarrier已破损,则抛出BrokenBarrierException异常
    		if (g.broken)
    			throw new BrokenBarrierException();
    
    		//如果当前线程已经中断,则将CyclicBarrier标记为已破损并抛出InterruptedException异常
    		if (Thread.interrupted()) {
    			breakBarrier();
    			throw new InterruptedException();
    		}
    
    		int index = --count;
    		//index == 0表示所有线程都到达了屏障点
    		if (index == 0) {  // tripped
    			boolean ranAction = false;
    			try {
    				//执行线程到齐后需要执行的任务
    				final Runnable command = barrierCommand;
    				if (command != null)
    					command.run();
    				ranAction = true;
    				//唤醒所有等待的线程并重置CyclicBarrier
    				nextGeneration();
    				return 0;
    			} finally {
    				if (!ranAction)
    					breakBarrier();
    			}
    		}
    
    		//线程没到齐,阻塞当前线程
    		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();
    
    			if (g != generation)
    				return index;
    
    			if (timed && nanos <= 0L) {
    				breakBarrier();
    				throw new TimeoutException();
    			}
    		}
    	} finally {
    		lock.unlock();
    	}
    }
    

    nextGeneration方法:

    private void nextGeneration() {
    	//唤醒所有等待的线程
    	trip.signalAll();
    	//重置CyclicBarrier
    	count = parties;
    	generation = new Generation();
    }
    
  • 相关阅读:
    【贴吧】计算器代码注释
    简单理解面向对象思维
    [转载]编程哲理
    Android SDK Manager无法更新的解决方案
    CSS选择器
    javascript arguments参数问题
    html垂直居中
    x64系统WSC注册方法
    SharePoint Server 2013安装
    Asp Url汉字乱码的问题
  • 原文地址:https://www.cnblogs.com/seve/p/14610932.html
Copyright © 2020-2023  润新知