之前说过AQS 是所有同步器的基础,那么我们就来说说那些基于AQS的同步工具吧
1、Semaphore 信号量
Semaphore(信号量)是一个控制并发数量两的同步器(吐槽:这名字和功能有毛线关系啊),他的构造接受一个int 参数,代表着并发数,当并发数,超过这个数就去队列中排队吧(这和读写锁中 读锁不是异曲同工吗,只不过读锁无法设置共享的线程数)。原理很好理解的吧,下面看看用法:
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
try {
String name = Thread.currentThread().getName();
System.out.println(name + " 我来上厕所了");
boolean b = semaphore.tryAcquire();
if (!b){
System.out.println(name + " 我没抢到坑位了");
}
System.out.println(name + " 我抢到一个坑,还剩" + semaphore.availablePermits() + "坑位");
Thread.sleep(3000);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
这段程序 创建了一个信号量 控制并发数5个,启动六个线程取上厕所。相当于六个人去抢五个厕所啊!我们看看结果
![](https://img.haomeiwen.com/i10911006/8cb18d470338309d.png)
2号线程开始没有抢到,被人用完了,才抢到的,要是没有用信号量控制,岂不是2号线程要在别人头上拉屎了吗?
综上:Semaphore 是和用于资源有限的共享情况,比如数据库连接池等。
2、CountDownLatch 计数器
CountDownLatch 是一个计数器,当计数器不到阈值(0)程序会一直等待。CountDownLatch 和Semaphore 提供了一个带有int参数的构造器。提供了一个countDown() (每次调用计数器减一)。他适合在多个任务需要相互协作的场景,比如 王者荣耀 需要五个人才可以开始游戏。我们可以设置一个数量为5的计数器,每一个人进入游戏就countDown 一次,当计数器为0的时候,游戏就可以开始啦。
下面看看用法:
public static void main(String[] args) throws Exception {
final CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 1; i < 6; i++){
final int num = i;
new Thread(() -> {
System.out.println("我是第" + num +" 个玩家,我进入游戏啦。");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
}).start();
}
//等待计数器到达阈值
countDownLatch.await();
System.out.println("终于有五个人了啊,开始游戏吧!");
}
countDownLatch.await() 这个方法会一直等待计数器到达阈值,如果这个方法只有四个人进入游戏,那么这局游戏永远无法开始啦,不信你试试。
3、CyclicBarrier 回环栅栏
之前用游戏这个案例来说明CountDownLatch 的用法,但是我们发现一个很严重的问题,那就是游戏是要一直开始,每五个人就要开始一局的,那么计数器用一次岂不是用不了了? 别担心 我们还有CyclicBarrier 。
CyclicBarrier 在我看来就是一个可以无限使用的计数器,它只要每达到阈值就会重新开始,
下面看看用法吧
public static void main(String[] args) throws Exception {
String[] arr = {"小明", "小陈", "小牛", "大神", "菜鸡"};
//CyclicBarrier 构造器 接受两个参数 1、阈值 2、到达阈值后的操作(为空则什么都不做)
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {
System.out.println("有五个玩家加入游戏,游戏开始!!!!!!!!");
});
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(new Random().nextInt(10000));
System.out.println(Thread.currentThread().getName() + "加入排位,总共有" + (cyclicBarrier.getNumberWaiting() + 1) + "玩家在等待");
//回环等待,等到阈值
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, arr[i / 5]);
thread.start();
}
}
看看结果:
![](https://img.haomeiwen.com/i10911006/408efe2e35dbaca5.png)
从结果上看 每五个人进入游戏,CyclicBarrier 就只执行一次 事先内置的任务。
使用上他和CountDownLatch 还是很有很大差别的。CountDownLatch是计算器,只有煮线程在等待计数器结果,其他线程处理完任务就完事了,而CyclicBarrier 是一个屏障,所有的线程遇到屏障都会停下来,等到集齐突破屏障的条件(例子中是五人进入游戏) 在一起处理,并且可以触发事先预留的处理事件(简直就像是集齐七颗龙珠召唤神龙一样 有木有!)。
网友评论