CountDownLatch,CyclicBarrier,Semaphore的使用及底层实现
今天我们就详细介绍一下JUC的一些常用同步工具类,减少计数(CountDownLatch),循环栅栏(CyclicBarrier),信号灯(Semaphore)的使用和区别。 CountDownLatch
作用 :就是一个或者多个线程在开始执行操作之前,必须要等到其他线程执行完成才可以执行。
类的构造方法 CountDownLatch(int count) 构造一个用给定计数初始化的值。 public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }
核心方法
countDown() 递减锁存器的计数,如果计数达到零,将释放所有等待的线程 public void countDown() { sync.releaseShared(1); }
await() 使当前线程在锁存器倒计数至零之前一直处于等待。 public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); }
当线程调用 await()方法时,就会阻塞当前线程。当线程调用一次countDown()方法时,count 就会减一,直到当count 的值等于0时候,被阻塞的线程才可以继续执行。
现在我们用一个生活中的例子说明:学生时代,当我们在考试的时候,监考老师必须等到所有的学生交完卷子才可以离开,此时监考老师就相当于等待线程,而学生就好比是执行的线程。
我们用代码实现这个案例:
参加考试的学生10个, main线程就相当于监考老师 public class CountDownLatchDemo { private static CountDownLatch countDownLatch = new CountDownLatch(10); public static void main(String[] args) throws InterruptedException { for (int i = 1; i <= 10; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+" 学生交卷:"); // 减一 countDownLatch.countDown(); } },String.valueOf(i)).start(); } // 等待 countDownLatch.await(); System.out.println(Thread.currentThread().getName()+" 监考老师离开教室"); } }
底层实现原理
从源码我们不难发现 CountDownLatch 是基于AQS实现的,当我们在构建CountDownLatch 对象时,传入的值其实就会赋值给 AQS 的关键变量state,执行**countDown()方法时,其实就是利用CAS 将state 减一,执行 await()**方法时,其实就是判断state是否为0,不为0则加入到队列中,将该线程阻塞掉(除了头节点),因为头节点会一直自旋等待state为0,当state为0时,头节点把剩余的在队列中阻塞的节点也一并唤醒。 CyclicBarrier
作用:N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。
常用的构造方法有: CyclicBarrier(int parties,Runnable barrierAction) 创建一个新的CyclicBarrier,它将在给定数量的线程处于等待状态时启动,并在启动barrier时执行给定的屏障操作,该操作由最后一个进入barrier的线程操作 public CyclicBarrier(int parties, Runnable barrierAction) { if (parties <= 0) throw new IllegalArgumentException(); this.parties = parties; this.count = parties; this.barrierCommand = barrierAction; }
核心方法
await() 在所有的参与者都已经在此barrier上调用await方法之前一直等待 public int await() throws InterruptedException, BrokenBarrierException { try { return dowait(false, 0L); } catch (TimeoutException toe) { throw new Error(toe); // cannot happen } }
生活中的例子:在打王者的时候,在开局前所有人都必须要加载到100%才可以进入。否则所有玩家都相互等待。 public class CyclicBarrierDemo { public static void main(String[] args) { CyclicBarrier cyclicBarrier =new CyclicBarrier(5, () -> System.out.println("游戏开始")); for (int i = 1; i <=5 ; i++) { int finalI = i; new Thread(new Runnable() { @Override public void run() { System.out.println("第"+ finalI +"进入游戏"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } },String.valueOf(i)).start(); } } }
总结:CyclicBarrier 的构造方法第一个参数是目标障碍数,每次执行 CyclicBarrier 一次障碍数会加一,如果达到了目标障碍数,才会执行 await()之后方法。
底层实现原理
从源码不难发现的是,它没有像 CountDownLatch 和ReentrantLock 使用AQS的state变量,而CyclicBarrier 是直接借助ReentrantLock 加上Condition 等待唤醒的功能 进而实现的。
在构建 CyclicBarrier 时,传入的值会赋值给CyclicBarrier 内部维护count 变量,也会赋值给parties 变量,每次调用await()方法时,会将count 减一 ,操作count值是直接使用ReentrantLock 来保证线程安全性。如果count不为0,则添加Condition 队列中,如果count等于0时,则把节点从Condition 队列添加至AQS的队列中进行全部唤醒,并且将parties的值重新赋值为count的值。 Semaphore
信号量,用来控制同一时间,资源可被访问的线程数量,一般应用场景流量的控制。
构造方法 Semaphore(int permits) 创建具有给定的许可数和非公平的公平设置的Semapore public Semaphore(int permits) { sync = new NonfairSync(permits); }
核心方法 acquire() 从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断,release() 释放一个许可,将其返回给信号量,设置许可数量Semaphore semaphore = new Semaphore(3) ,一般acquire() 都会抛出异常,release 在finally 中执行。 public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1); }public void release() { sync.releaseShared(1); }
举例说明,6辆强站三个停车位。 public class SemaphoreDemo { public static void main(String[] args) { // 模拟资源类,有3个空车位 Semaphore semaphore = new Semaphore(3); for (int i = 1; i <=6 ; i++) { new Thread(() -> { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName()+" 抢到了车位"); TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }finally { semaphore.release(); } System.out.println(Thread.currentThread().getName()+" 离开了车位"); semaphore.release(); },String.valueOf(i)).start(); } } }
Semaphore 底层实现是基于AQS实现的类似于 CountDownLatch 总结
CountDownLatch ,Semaphore 都是基于AQS实现。
CountDownLatch 是一个线程等待其他线程,CyclicBarrier 是线程之间相互等待。
CountDownLatch 会将构造CountDownLatch 的入参传递至state,**countDown()就是在利用CAS将state减一, await()**实际就是让头节点一直在等待state为0时,释放所有等待的线程。
CyclicBarrier 则利用ReentrantLock 和Condition ,自身维护了count和parties变量。每次调用await将count减一,并将线程加入到Condition 队列上。等到count为0时,则将Condition 队列的节点移交至AQS队列,并全部释放。
一个家庭最好的规矩,离不开这四点!(说得真好)活在这个世上,几乎每个人都想要幸福快乐地生活,想日子过得富裕,一家人其乐融融地在一起,当然想是一回事,能不能实现则是另一回事。很多人以为家庭中只要有钱了就会幸福,实际上这是片面的,
人间清醒傅首尔金句合集当30岁还在为10块打车费纠结,下雨天没人送伞,下班再晚没人询问,你就会明白,爱不爱有什么用,谋生才是体面当孩子有100颗糖时,你怎么会介意分享,可当他只有两颗糖,你又凭什么叫他大
少动情,多挣钱阅读本文前,点击上方卡片一键关注Episode02366去爱那个拿真金白银对你好的人爱是行动不是光靠嘴爱这个东西如果不能锦上添花不能雪中送碳真的没必要来苦练七十二变才能笑对八十一难
内心的黑狗达我在岛屿读书三十多年过去了,我突然回想起来在小镇的童年生活。大抵那个九十年代的小镇,我们所经历的生活,都是整个时代的缩影。如今的妈妈,依旧会用祈求神灵的方式,来对待我的孩子。我默不
简谈我的同学李杰作为一名成年人,内心已经没有了年少时的脆弱。幸运的是,校园环境可以包容少年的脆弱,不幸的是,目前校园里还存在着多多少少的校园欺凌问题。大部分人都会对被欺负者产生同情之心,对施暴者深
卫浴匠心青春力量丨2022中国民族卫浴发展峰会在佛山盛大召开11月9日,卫浴匠心青春力量2022中国民族卫浴发展峰会在广东佛山盛大召开。来自政府行业协会企业家和媒体在内的500余名业内人士同聚一堂,共话卫浴行业新机遇见证卫浴行业大国品牌,着
新研究发现银河系中最古老行星碎片环球网科技综合报道11月8日消息,据外媒报道,由英国华威大学领导的天文学家们团队发现一颗距离地球90光年的微弱白矮星及其轨道行星系统的残骸,它们已有超过100亿年的历史。在这项研究
台积电突然发话,不能出货就中企背锅?芯片专家咎由自取在市场规则下,台积电选择了遵从,所以失去了第二大客户订单,也因此对美国客户产生订单依赖。美国一次次修改规则,台积电的态度也非常明确,把美国客户订单放在了第一位。而台积电再次摆正了芯
中国航天新纪录!西昌卫星发射中心完成第200次发射任务11月12日上午,我国在文昌航天发射场使用长征七号运载火箭,成功发射天舟五号货运飞船。这是文昌航天发射场所在的西昌卫星发射中心完成的第200次发射任务,该中心也成为我国首个突破20
6连胜!NBA夺冠热门大爆发,即将登顶联盟第1,头号巨星恐怖升级北京时间11月13日,NBA常规赛凯尔特人117108击败活塞,他们悄无声息地拿下了6连胜,距离登顶联盟第一已经只剩下了0。5个胜场。而在多项数据上,凯尔特人也领衔全联盟30支队伍
竞赛题6235。逐层排序二叉树所需的最少操作数目头条创作挑战赛题目给你一个值互不相同的二叉树的根节点root。在一步操作中,你可以选择同一层上任意两个节点,交换这两个节点的值。返回每一层按严格递增顺序排序所需的最少操作数目。节点