1 减少计数CountDownLatch
1.1 CountDownLatch 概述
CountDownLatch
是 Java 并发包(java.util.concurrent
)中的一个同步工具类,用于在多个线程之间进行协调。它允许一个或多个线程等待其他线程完成一组操作。
1.1.1 主要方法
CountDownLatch(int count)
:构造方法,初始化一个计数器,计数器的初始值为count
。void countDown()
:将计数器的值减 1。void await()
:使当前线程等待,直到计数器的值变为 0。boolean await(long timeout, TimeUnit unit)
:使当前线程等待,直到计数器的值变为 0,或者等待超时。
1.1.2 工作原理
- 初始化计数器:通过构造方法
CountDownLatch(int count)
初始化一个计数器,计数器的初始值为count
。 - 线程等待:一个或多个线程调用
await()
方法,这些线程会阻塞,直到计数器的值变为 0。 - 计数器减 1:其他线程调用
countDown()
方法,将计数器的值减 1。调用countDown()
方法的线程不会阻塞。 - 唤醒等待线程:当计数器的值变为 0 时,因
await()
方法阻塞的线程会被唤醒,继续执行。
1.2 示例代码
场景: 6 个同学陆续离开教室后值班同学才可以关门
public class CountDownLatchDemo {
// 6个同学陆续离开教室之后,班长锁门
public static void main(String[] args) throws InterruptedException {
// 创建CountDownLatch对象,设置初始值
int count = 6;
CountDownLatch countDownLatch = new CountDownLatch(count);
// 6个同学陆续离开教室之后
for (int i = 1; i <= count; 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.2.1 代码解释
- 初始化计数器:
int count = 6;
CountDownLatch latch = new CountDownLatch(count);
- 创建并启动三个线程:
for (int i = 1; i <= count; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName() + " 号同学离开了教室");
// 计数器 -1
countDownLatch.countDown();
},String.valueOf(i)).start();
}
- 主线程等待:
// 主线程等待,直到计数器变为 0
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "班长锁门了");
1.3 小结
CountDownLatch
是一个非常有用的同步工具类,用于协调多个线程之间的执行顺序。通过初始化一个计数器,并使用 countDown()
和 await()
方法,可以实现线程之间的等待和唤醒机制。在实际应用中,CountDownLatch
常用于等待一组线程完成某个任务后再继续执行后续操作。
2 循环栅栏CyclicBarrier
2.1 CyclicBarrier 概述
CyclicBarrier
是 Java 并发包(java.util.concurrent
)中的一个同步工具类,用于协调多个线程之间的执行顺序。它允许一组线程互相等待,直到所有线程都到达一个共同的屏障点(barrier point),然后继续执行。
2.1.1 主要方法
CyclicBarrier(int parties)
:构造方法,初始化一个CyclicBarrier
,指定需要等待的线程数量。CyclicBarrier(int parties, Runnable barrierAction)
:构造方法,初始化一个CyclicBarrier
,指定需要等待的线程数量,并在所有线程到达屏障点时执行一个指定的Runnable
任务。int await()
:使当前线程等待,直到所有线程都调用了await()
方法,或者某个线程被中断。int await(long timeout, TimeUnit unit)
:使当前线程等待,直到所有线程都调用了await()
方法,或者某个线程被中断,或者等待超时。int getParties()
:返回需要等待的线程数量。boolean isBroken()
:查询此屏障是否处于损坏状态。void reset()
:将屏障重置为其初始状态。
2.1.2 工作原理
- 初始化屏障:通过构造方法
CyclicBarrier(int parties)
或CyclicBarrier(int parties, Runnable barrierAction)
初始化一个CyclicBarrier
,指定需要等待的线程数量。 - 线程等待:每个线程调用
await()
方法,等待其他线程到达屏障点。 - 屏障点触发:当所有线程都调用了
await()
方法,屏障点被触发,所有线程继续执行。 - 可选的屏障动作:如果构造方法中指定了
Runnable
任务,则在所有线程到达屏障点后,执行该任务。 - 重用屏障:
CyclicBarrier
可以重复使用,当所有线程到达屏障点后,屏障会自动重置,等待下一组线程到达。
2.2 示例代码
场景: 集齐 7 颗龙珠就可以召唤神龙
public class CyclicBarrierDemo {
// 创建固定值
private static final int NUMBER = 7;
public static void main(String[] args) {
// 创建
CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
System.out.println("集齐七颗龙珠就可以召唤神龙");
});
// 集齐七颗龙珠过程
for (int i = 1; i <= 7; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName() + " 星龙珠被收集了");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
},String.valueOf(i)).start();
}
}
}
2.2.1 代码解释
- 初始化屏障:
private static final int NUMBER = 7;
CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
System.out.println("集齐七颗龙珠就可以召唤神龙");
});
- 创建并启动七个线程:
for (int i = 1; i <= 7; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName() + " 星龙珠被收集了");
try {
// 等待其他线程
cyclicBarrier.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
},String.valueOf(i)).start();
}
2.3 小结
CyclicBarrier
是一个非常有用的同步工具类,用于协调多个线程之间的执行顺序。通过初始化一个屏障,并使用 await()
方法,可以实现线程之间的等待和唤醒机制。CyclicBarrier
可以重复使用,适用于需要多个线程协同工作的场景。
3 信号灯Semaphore
3.1 Semaphore 概述
Semaphore
是 Java 并发包(java.util.concurrent
)中的一个同步工具类,用于控制同时访问某个资源的线程数量。它通过许可证(permit)的概念来实现,每个许可证代表一个线程可以访问资源的权限。
3.1.1 主要方法
Semaphore(int permits)
:构造方法,初始化一个Semaphore
,指定最大信号量(许可证数量)。Semaphore(int permits, boolean fair)
:构造方法,初始化一个Semaphore
,指定最大信号量(许可证数量),并设置是否使用公平锁(fairness)。void acquire()
:获取一个许可证,如果没有可用的许可证,则阻塞当前线程,直到有许可证可用。void acquire(int permits)
:获取指定数量的许可证,如果没有足够的许可证,则阻塞当前线程,直到有足够的许可证可用。void release()
:释放一个许可证,将其返回给信号量。void release(int permits)
:释放指定数量的许可证,将其返回给信号量。int availablePermits()
:返回当前可用的许可证数量。boolean tryAcquire()
:尝试获取一个许可证,如果成功则返回true
,否则返回false
。boolean tryAcquire(int permits)
:尝试获取指定数量的许可证,如果成功则返回true
,否则返回false
。boolean tryAcquire(long timeout, TimeUnit unit)
:尝试在指定时间内获取一个许可证,如果成功则返回true
,否则返回false
。boolean tryAcquire(int permits, long timeout, TimeUnit unit)
:尝试在指定时间内获取指定数量的许可证,如果成功则返回true
,否则返回false
。
3.1.2 工作原理
- 初始化信号量:通过构造方法
Semaphore(int permits)
或Semaphore(int permits, boolean fair)
初始化一个Semaphore
,指定最大信号量(许可证数量)。 - 获取许可证:线程调用
acquire()
方法获取一个许可证,如果没有可用的许可证,则阻塞当前线程,直到有许可证可用。 - 释放许可证:线程调用
release()
方法释放一个许可证,将其返回给信号量。 - 控制并发访问:通过控制许可证的数量,可以限制同时访问某个资源的线程数量。
3.2 示例代码
场景: 抢车位, 6 部汽车 3 个停车位
public class SemaphoreDemo {
public static void main(String[] args) {
// 创建Semaphore,设置许可数量
int permits = 3;
Semaphore semaphore = new Semaphore(permits);
// 模拟6辆汽车
for (int i = 1; i <= 6; i++) {
new Thread(()->{
try {
//抢占
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"抢到了车位");
//设置随机停车时间
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
System.out.println(Thread.currentThread().getName() + " ----离开了车位");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
// 释放
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
代码解释
- 初始化信号量:
int permits = 3;
Semaphore semaphore = new Semaphore(permits);
- 创建并启动五个线程:
for (int i = 1; i <= 6; i++) {
new Thread(()->{
try {
//抢占
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"抢到了车位");
//设置随机停车时间
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
System.out.println(Thread.currentThread().getName() + " ----离开了车位");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
// 释放
semaphore.release();
}
},String.valueOf(i)).start();
}
3.3 小结
Semaphore
是一个非常有用的同步工具类,用于控制同时访问某个资源的线程数量。通过初始化一个信号量,并使用 acquire()
和 release()
方法,可以实现对并发访问的控制。Semaphore
适用于需要限制并发访问数量的场景,如资源池、连接池等。
4 思维导图
5 参考链接
【【尚硅谷】大厂必备技术之JUC并发编程】