并发编程--CountdownLatch && CyclicBarrier

Semaphore,CountdownLatch,CyclicBarrier都是java提供的同步辅助类。
上一篇对Semaphore有了一定的了解,本篇则针对CountdownLatch以及CyclicBarrier进行一定的总结。

一、CountdownLatch

1)CountdownLatch是什么?

CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)
能够使一个线程等待其他线程完成各自的工作之后再执行。例如zookeeper分布式锁的实现。而Semaphore则主要强调的是资源有限。

2)CountdownLatch使用场景

比如老公陪同媳妇去医院,媳妇等候医生看病,老公则针对单子去拿药,两者都完工,则一起回家。再比如老板等10个员工开会,只有10个员工都到齐了,会议才能开始等等场景。

3)应用demo

举例说明老板等待员工开会的场景:

public static void main(String[] args) {
		//老板需要等待15个员工会议室开会
		final CountDownLatch latch = new CountDownLatch(15);
		for (int i = 0; i < 15; i++) {
			Random random = new Random();
			final int timer = random.nextInt(1000);
			new Thread(() -> {
				try {
					System.out.println("子线程" + Thread.currentThread().getName() + "正在赶路");
					//模仿每个员工走自己线程需要的时间
					Thread.sleep(1000 + timer);
					//调用latch的countDown方法使计数器-1;一共15个
					latch.countDown();
					System.out.println("子线程" + Thread.currentThread().getName() + "到会议室了");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}).start();
		}

		try {
			System.out.println("领导等待员工会议室开会...");
			//主线程阻塞等待计数器归零
			latch.await();
			System.out.println("员工都来了,会议开始");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

执行结果:

子线程Thread-1正在赶路
子线程Thread-3正在赶路
子线程Thread-0正在赶路
子线程Thread-2正在赶路
子线程Thread-4正在赶路
子线程Thread-5正在赶路
子线程Thread-6正在赶路
子线程Thread-7正在赶路
子线程Thread-8正在赶路
子线程Thread-9正在赶路
子线程Thread-10正在赶路
子线程Thread-11正在赶路
子线程Thread-12正在赶路
子线程Thread-13正在赶路
领导等待员工会议室开会…
子线程Thread-14正在赶路
子线程Thread-5到会议室了
子线程Thread-1到会议室了
子线程Thread-12到会议室了
子线程Thread-13到会议室了
子线程Thread-4到会议室了
子线程Thread-6到会议室了
子线程Thread-2到会议室了
子线程Thread-0到会议室了
子线程Thread-3到会议室了
子线程Thread-10到会议室了
子线程Thread-14到会议室了
子线程Thread-8到会议室了
子线程Thread-9到会议室了
子线程Thread-7到会议室了
子线程Thread-11到会议室了
员工都来了,会议开始

4)源码分析

核心关注方法只有两个,latch.countDown();和latch.await();
countDown() 方法每次调用都会将 state 减 1,直到state 的值为 0;而 await 是一个阻塞方法,当 state 减 为 0 的时候,await 方法才会返回。await 可以被多个线程调用,大家在这个时候脑子里要有个图:所有调用了await 方法的线程阻塞在 AQS 的阻塞队列中,等待条件满(state == 0),将线程从队列中一个个唤醒过来。

public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

在 CountDownLatch 内部写了一个 Sync 并且继承了 AQS 这个抽象类重写了 AQS中的共享锁方法。这段代码主要是判定当前线程是否获取到了共享锁
在这里插入图片描述

public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        //state 如果不等于 0,说明当前线程需要加入到共享锁队列中
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
//在共享可中断模式下获取。
private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        //创建一个共享模式的节点添加到队列中
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    //尝试获取锁
                    int r = tryAcquireShared(arg);
                    //r>=0 表示获取到了执行权限,这个时候因为 state!=0,所以不会执行这段代码
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                //阻塞线程
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

二、CyclicBarrier

1)CyclicBarrier是什么?

栅栏屏障,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。
简单理解就是:多个线程之间互相等待,满足条件在同一时间进行。

2)CyclicBarrier使用场景

可以用于多线程计算数据,最后合并计算结果的场景。
又比如旅行团旅行,需要大家都到齐,证件都办理好,才能出发。

3)应用demo

旅行团旅行,需要大家都到齐,证件都办理好,上旅行车才能出发。

public class CyclicBarrierTest1 extends Thread{
	private final CyclicBarrier barrier;
	//随机值处理
	private final Random random = new Random();
	public CyclicBarrierTest1(String name,CyclicBarrier barrier) {
		super(name);
		this.barrier = barrier;
	}

	/**
	 * 重写run方法
	 */
	@Override
	public void run() {
		try {
			Thread.sleep(random.nextInt(2000));
			System.out.println(Thread.currentThread().getName() + " - 已经到达旅行团");
			barrier.await();
			Thread.sleep(random.nextInt(2000));
			System.out.println(Thread.currentThread().getName() + " - 证件已经办理好");
			barrier.await();
			Thread.sleep(random.nextInt(2000));
			System.out.println(Thread.currentThread().getName() + " - 已经上旅行车");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (BrokenBarrierException e) {
			e.printStackTrace();
		}
		super.run();
	}

	public static void main(String[] args) {
		//旅行团5个游客
		CyclicBarrier cyclicBarrier=new CyclicBarrier(5);
		for (int i=0;i<5;i++){
			new CyclicBarrierTest1("游客-" + (i + 1), cyclicBarrier).start();
		}
	}
}

游客-5 - 已经到达旅行团
游客-3 - 已经到达旅行团
游客-2 - 已经到达旅行团
游客-1 - 已经到达旅行团
游客-4 - 已经到达旅行团
游客-2 - 证件已经办理好
游客-4 - 证件已经办理好
游客-1 - 证件已经办理好
游客-3 - 证件已经办理好
游客-5 - 证件已经办理好
游客-1 - 已经上旅行车
游客-2 - 已经上旅行车
游客-3 - 已经上旅行车
游客-5 - 已经上旅行车
游客-4 - 已经上旅行车

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值