JUC——线程同步辅助工具类(Semaphore,CountDownLatch,Cyclic
CountDownLatchCountDownLatch是一个计数器闭锁,通过它可以完成类似于阻塞当前线程的功能,即:一个线程或多个线程一直等待,直到其他线程执行的操作完成。CountDownLatch用一个给定的计数器来初始化,该计数器的操作是原子操作,即同时只能有一个线程去操作该计数器。调用该类await方法的线程会一直处于阻塞状态,直到其他线程调用countDown方法使当前计数器的值变为零,每次调用countDown计数器的值减1。当计数器值减至零时,所有因调用await()方法而处于等待状态的线程就会继续往下执行。这种现象只会出现一次,因为计数器不能被重置,如果业务上需要一个可以重置计数次数的版本,可以考虑使用CycliBarrier。 在某些业务场景中,程序执行需要等待某个条件完成后才能继续执行后续的操作;下面举个栗子,比如秦国需要灭六国才能一统华夏。 代码示例: public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 1; i <= 6; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + "t国,被灭"); countDownLatch.countDown(); },Objects.requireNonNull(CountryEnum.forEachCountryEnum(i)).getRetMessage()).start(); } countDownLatch.await(); System.out.println(Thread.currentThread().getName() + "t秦灭六国,一统华夏"); } } public enum CountryEnum { ONE(1,"齐"),TWO(2,"楚"),THREE(3,"燕"),FOUR(4,"赵"),FIVE(5,"魏"),SIX(6,"韩"); private Integer retCode; private String retMessage; CountryEnum(Integer retCode,String retMessage) { this.retCode = retCode; this.retMessage = retMessage; } public static CountryEnum forEachCountryEnum(int index) { CountryEnum[] values = CountryEnum.values(); for (CountryEnum value : values) { if (index == value.getRetCode()) { return value; } } return null; } public Integer getRetCode() { return retCode; } public String getRetMessage() { return retMessage; }} CyclicBarrierCyclicBarrier(可重用屏障/栅栏)类似于CountDownLatch(倒计数闭锁),它能阻塞一组线程直到某个事件的发生。
举个栗子: public class CyclicBarrierDemo { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(7,() -> System.out.println("召唤神龙")); for (int i = 1; i <= 7; i++) { final int tempInt = i; new Thread(() -> { System.out.println(Thread.currentThread().getName() + "t收集到第: " + tempInt + "龙珠"); try { cyclicBarrier.await(); } catch (Exception e) { e.printStackTrace(); } },i + "").start(); } } } Semaphore信号量Semaphore是一个控制访问多个共享资源的计数器,和CountDownLatch一样,其本质上是一个“共享锁”。 Semaphore,在API是这么介绍的: 一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore只对可用许可的号码进行计数,并采取相应的行动。 下面我们就一个停车场的简单例子来阐述Semaphore: 假设停车场仅有5个停车位,一开始停车场没有车辆所有车位全部空着,然后先后到来三辆车,停车场车位够,安排进去停车,然后又来三辆,这个时候由于只有两个停车位,所有只能停两辆,其余一辆必须在外面候着,直到停车场有空车位,当然以后每来一辆都需要在外面候着。当停车场有车开出去,里面有空位了,则安排一辆车进去(至于是哪辆 要看选择的机制是公平还是非公平)。 从程序角度看,停车场就相当于信号量Semaphore,其中许可数为5,车辆就相对线程。当来一辆车时,许可数就会减 1 ,当停车场没有车位了(许可书 == 0 ),其他来的车辆需要在外面等候着。如果有一辆车开出停车场,许可数 + 1,然后放进来一辆车。 号量Semaphore是一个非负整数(>=1)。当一个线程想要访问某个共享资源时,它必须要先获取Semaphore,当Semaphore >0时,获取该资源并使Semaphore – 1。如果Semaphore值 = 0,则表示全部的共享资源已经被其他线程全部占用,线程必须要等待其他线程释放资源。当线程释放资源时,Semaphore则+1 代码演示: public class SemaphoreDemo { public static void main(String[] args) { // 模拟5个停车位,10辆车去抢车位 // Semaphore内部包含公平锁(FairSync)和非公平锁(NonfairSync)默认为非公平锁 Semaphore semaphore = new Semaphore(5); // 模拟六部汽车 for (int i = 1; i <= 10; i++) { new Thread(() -> { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + "t抢到车位"); Thread.sleep(3000); System.out.println(Thread.currentThread().getName() + "t停车3秒后,离开车位"); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } },i + "").start(); } } } CyclicBarrier、CountDownLatch、Semaphore 的小总结
如果觉得对你有帮助,欢迎来访我的博客:http://www.jianjieming.vip (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |