JUC(4) 辅助类
2023-12-29 00:26:16 # Language # Java

1. 减少计数 CountDownLatch

CountDownLatch 类可以设置一个计数器,然后通过 countDown 方法来进行减 1 的操作,使用 await 方法等待计数器不大于 0,然后继续执行 await 方法之后的语句。

  • CountDownLatch 主要有两个方法,当一个或多个线程调用 await 方法时,这些线程会阻塞
  • 其它线程调用 countDown 方法会将计数器减 1 (调用 countDown 方法的线程不会阻塞)
  • 当计数器的值变为 0 时,因 await 方法阻塞的线程会被唤醒,继续执行

案例

6个同学陆续离开教室之后,班长才能锁门。如果不加 CountDownLatch类,会出现线程混乱执行,同学还未离开教室班长就已经锁门了

不使用CountDownLatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
// 创建六个线程,模拟六个学生
for (int i = 1; i <= 6; i++) {
new Thread(() -> System.out.println(Thread.currentThread().getName() + "离开教室"),String.valueOf(i)).start();
}
System.out.println(Thread.currentThread().getName()+"锁门");
}
/*
1离开教室
4离开教室
2离开教室
6离开教室
3离开教室
main锁门
5离开教室
*/

使用CountDownLatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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()+"离开教室");
// 计数 -1
countDownLatch.countDown();
},String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+"锁门");
}
/*
1离开教室
5离开教室
4离开教室
6离开教室
2离开教室
3离开教室
main锁门
*/

2. 循环栅栏 CyclicBarrier

CyclicBarrier 是一个同步铺助类,它允许一组线程互相等待,直到到达某个公共屏障点(common barrier point)。因为该 barrier 在释放等待线程后可以重用,所以称它为循环的 barrier。

CyclicBarier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。

  • 默认的构造方法是 CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用 await 方法告诉 CyclicBarrier 已经到达屏障位置,线程被阻塞。
  • 另外一个构造方法 CyclicBarrier(int parties, Runnable barrierAction),其中 barrierAction 任务会在所有线程到达屏障后执行。

  • await(): 参与者在所有的参与者都已经在此 barrier 上调用 await 方法之前一直等待

案例

集齐7颗龙珠就可以召唤神龙

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class CyclicBarrirtTest {
// 创建固定值
private static final int NUMBER = 7;
public static void main(String[] args) {
// 每次执行 CyclicBarrier 一次障碍数会加一,如果达到了目标障碍数,才会执行 cyclicBarrier.await()之后的语句。
CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
System.out.println("****集齐7颗龙珠就可以召唤神龙");
});
for (int i = 1; i <= 7; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"星龙被收集到了");
try { // 计数 +1
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
}
/*
1星龙被收集到了
4星龙被收集到了
5星龙被收集到了
3星龙被收集到了
2星龙被收集到了
6星龙被收集到了
7星龙被收集到了
****集齐7颗龙珠就可以召唤神龙
*/

3. 信号量 Semaphore

一个计数信号量,从概念上讲,信号量维护了一个许可集,如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动

Semaphore 通常用于限制可以访问某些资源的线程数目

常用的构造方法

  • Semaphore(int permits) 创建具有给定许可数和非公平设置的Semaphore
  • Semaphore(int permits, boolean fair)

常用方法

  • acquire() 从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断
  • release() 释放一个许可,将其返回给信号量

案例

6辆汽车,停3个车位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public static void main(String[] args) {
//创建Semaphore,设置许可数量
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(new Random().nextInt(3));
// 离开车位
System.out.println(Thread.currentThread().getName() + "------离开了车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
/*
1抢到了车位
3抢到了车位
2抢到了车位
1------离开了车位
4抢到了车位
3------离开了车位
5抢到了车位
5------离开了车位
6抢到了车位
6------离开了车位
4------离开了车位
2------离开了车位
*/