最近学习了Java并发编程中的CountDownLatch、CyclicBarrier、Semaphore,做个小总结。
这三个类都是java1.5中提供的一些非常有用的辅助类,用于帮助程序员实现并发编程。
CountDownLatch
- 背景
- CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。
- CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。
- countDownLatch是在java1.5被引入,跟它一起被引入的工具类还有CyclicBarrier、Semaphore、concurrentHashMap和BlockingQueue。
- 存在于java.util.cucurrent包下。
- 概念
- countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
- 是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
- 源码
- countDownLatch类中只提供了一个构造器
//参数count为计数值 public CountDownLatch(int count) { };
- 类中有三个方法最重要
//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行 public void await() throws InterruptedException { }; //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行 public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //将count值减1 public void countDown() { };
- countDownLatch类中只提供了一个构造器
- 实例
import java.util.concurrent.CountDownLatch; 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(); }, CountryEnum.forEach_CountryEnum(i).getRetMessage()).start(); } countDownLatch.await(); System.out.println(Thread.currentThread().getName() + "\t 六国灭"); //System.out.println(CountryEnum.SIX); //System.out.println(CountryEnum.SIX.getRetCode()); //System.out.println(CountryEnum.SIX.getRetMessage()); } }
- 运行结果:
魏 国,被灭 齐 国,被灭 赵 国,被灭 韩 国,被灭 楚 国,被灭 燕 国,被灭 main 六国灭
CyclicBarrier
- 概念
-
允许一组线程全部等待彼此达到共同屏障点的同步辅助。 循环阻塞在涉及固定大小的线程方的程序中很有用,这些线程必须偶尔等待彼此。 屏障被称为循环 ,因为它可以在等待的线程被释放之后重新使用。
A
CyclicBarrier
支持一个可选的Runnable
命令,每个屏障点运行一次,在派对中的最后一个线程到达之后,但在任何线程释放之前。 在任何一方继续进行之前,此屏障操作对更新共享状态很有用。实现原理:在CyclicBarrier的内部定义了一个Lock对象,每当一个线程调用await方法时,将拦截的线程数减1,然后判断剩余拦截数是否为初始值parties,如果不是,进入Lock对象的条件队列等待。如果是,执行barrierAction对象的Runnable方法,然后将锁的条件队列中的所有线程放入锁等待队列中,这些线程会依次的获取锁、释放锁。
-
- 方法
- 构造方法
-
//创建一个新的 CyclicBarrier ,当给定数量的线程(线程)等待它时,它将跳闸,并且当屏障跳闸时不执行预定义的动作。 CyclicBarrier(int parties) //创建一个新的 CyclicBarrier ,当给定数量的线程(线程)等待时,它将跳闸,当屏障跳闸时执行给定的屏障动作,由最后一个进入屏障的线程执行。 CyclicBarrier(int parties, Runnable barrierAction)
-
- 常用方法
-
//等待所有 parties已经在这个障碍上调用了 await 。 int await() //等待所有 parties已经在此屏障上调用 await ,或指定的等待时间过去。 int await(long timeout, TimeUnit unit) //返回目前正在等待障碍的各方的数量。 int getNumberWaiting() //返回旅行这个障碍所需的parties数量。 int getParties() //查询这个障碍是否处于破碎状态。 boolean isBroken() //将屏障重置为初始状态。 void reset()
-
- 构造方法
- 示例
/** * @program: java8_demo * @description: CyclicBarrier的使用 * @author: XZQ * @create: 2020-01-06 10:43 **/ 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 (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }, String.valueOf(i)).start(); } } }
- 运行结果
3 收集第:3颗龙珠 2 收集第:2颗龙珠 6 收集第:6颗龙珠 4 收集第:4颗龙珠 1 收集第:1颗龙珠 7 收集第:7颗龙珠 5 收集第:5颗龙珠 召唤神龙
CountDownLatch和CyclicBarrier区别
-
CountDownLatch和CyclicBarrier都是java.util.concurrent包下面的多线程工具类。
-
CountDownLatch是一个计数器,线程完成一个记录一个,计数器递减,只能只用一次
-
CyclicBarrier的计数器更像一个阀门,需要所有线程都到达,然后继续执行,计数器递增,提供reset功能,可以多次使用
-
从字面上理解,CountDown表示减法计数,Latch表示门闩的意思,计数为0的时候就可以打开门闩了。Cyclic Barrier表示循环的障碍物。两个类都含有这一个意思:对应的线程都完成工作之后再进行下一步动作,也就是大家都准备好之后再进行下一步。然而两者最大的区别是,进行下一步动作的动作实施者是不一样的。这里的“动作实施者”有两种,一种是主线程(即执行main函数),另一种是执行任务的其他线程,后面叫这种线程为“其他线程”,区分于主线程。对于CountDownLatch,当计数为0的时候,下一步的动作实施者是main函数;对于CyclicBarrier,下一步动作实施者是“其他线程”。
Semaphore
- 概念
Semaphore是一种在多线程环境下使用的设施,该设施负责协调各个线程,以保证它们能够正确、合理的使用公共资源的设施,也是操作系统中用于控制进程同步互斥的量。Semaphore是一种计数信号量,用于管理一组资源,内部是基于AQS的共享模式。它相当于给线程规定一个量从而控制允许活动的线程数。
简单来说 Semaphore 是一个计数信号量,必须由获取它的线程释放。
常用于限制可以访问某些资源的线程数量,例如通过 Semaphore 限流。
- 方法
- 构造方法
//构造方法,创建具有给定许可数的计数信号量并设置为非公平信号量。 Semaphore(int permits) //构造方法,当fair等于true时,创建具有给定许可数的计数信号量并设置为公平信号量。 Semaphore(int permits,boolean fair)
- 常用方法
//从此信号量获取一个许可前线程将一直阻塞。相当于一辆车占了一个车位 void acquire() //从此信号量获取给定数目许可,在提供这些许可前一直将线程阻塞。比如n=2,就相当于一辆车占了两个车位 void acquire(int n) //释放一个许可,将其返回给信号量。就如同车开走返回一个车位 void release() //释放n个许可 void release(int n) //当前可用的许可数 int availablePermits()
- 构造方法
- 示例
/** * @program: java8_demo * @description: Semaphore使用 * @author: XZQ * @create: 2020-01-06 11:08 **/ public class SemaphoreDemo { public static void main(String[] args) { Semaphore semaphore = new Semaphore(3);//模拟3个车位 for (int i = 1; i <= 6; i++) { new Thread(() -> { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + "\t抢到车位"); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName() + "\t停车三秒后离开车位"); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }, String.valueOf(i)).start(); } } }
- 运行结果
4 抢到车位 2 抢到车位 3 抢到车位 4 停车三秒后离开车位 3 停车三秒后离开车位 2 停车三秒后离开车位 6 抢到车位 5 抢到车位 1 抢到车位 5 停车三秒后离开车位 1 停车三秒后离开车位 6 停车三秒后离开车位 如果将 Semaphore semaphore = new Semaphore(3);//模拟3个车位改成 Semaphore semaphore = new Semaphore(1);//模拟1个车位 就如同一个单例模式,即单个停车位,只有一辆车进,然后这辆车出来后,下一辆车才能进。 运行结果就成为 2 抢到车位 2 停车三秒后离开车位 3 抢到车位 3 停车三秒后离开车位 5 抢到车位 5 停车三秒后离开车位 6 抢到车位 6 停车三秒后离开车位 4 抢到车位 4 停车三秒后离开车位 1 抢到车位 若改为6 3 抢到车位 4 抢到车位 5 抢到车位 1 抢到车位 6 抢到车位 2 抢到车位 3 停车三秒后离开车位 4 停车三秒后离开车位 1 停车三秒后离开车位 6 停车三秒后离开车位 2 停车三秒后离开车位 5 停车三秒后离开车位
- 总结
- Semaphore主要用于控制当前活动线程数目,就如同停车场系统一般,而Semaphore则相当于看守的人,用于控制总共允许停车的停车位的个数,而对于每辆车来说就如同一个线程,线程需要通过acquire()方法获取许可,而release()释放许可。如果许可数达到最大活动数,那么调用acquire()之后,便进入等待队列,等待已获得许可的线程释放许可,从而使得多线程能够合理的运行。
枚举的使用
/**
* @program: java8_demo
* @description: 枚举的使用
* @author: XZQ
* @create: 2020-01-06 10:03
**/
public enum CountryEnum {
ONE(1, "齐"), TWO(2, "楚"), THREE(3, "燕"), FOUR(4, "赵"), FIVE(5, "魏"), SIX(6, "韩");
private Integer retCode;
private String retMessage;
public Integer getRetCode() {
return retCode;
}
public String getRetMessage() {
return retMessage;
}
CountryEnum(Integer retCode, String retMessage) {
this.retCode = retCode;
this.retMessage = retMessage;
}
public static CountryEnum forEach_CountryEnum(int index) {
CountryEnum[] countryEnums = CountryEnum.values();
for (CountryEnum element : countryEnums) {
if (index == element.getRetCode())
return element;
}
return null;
}
}
来源:CSDN
作者:甲如_0314
链接:https://blog.csdn.net/qq_42107430/article/details/103854488