一、Semaphore
Semaphore可以用作信号量,控制同时访问共享资源的线程数量上限!
public static void main(String[] args) {
// 1. 创建 semaphore 对象
Semaphore semaphore = new Semaphore(3);
// 2. 10个线程同时运行
for (int i = 0; i < 10; i++) {
new Thread(() -> {
// 3. 获取许可
try {
//每个线程首先都会尝试获取信号量
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
log.debug("running...");
sleep(1);
log.debug("end...");
} finally {
// 4. 释放许可
semaphore.release();
}
}).start();
}
}
二、CountdownLatch
CountdownLatch可以辅助线程进行同步协作,通过它可以实现让某个线程等待其它多个线程完成任务之后才能继续向下执行。其中构造参数用来初始化等待计数值,await() 用来等待计数归零,countDown() 用来让计数减一!
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
new Thread(() -> {
log.debug("begin...");
sleep(1);
latch.countDown();
log.debug("end...{}", latch.getCount());
}).start();
new Thread(() -> {
log.debug("begin...");
sleep(2);
latch.countDown();
log.debug("end...{}", latch.getCount());
}).start();
new Thread(() -> {
log.debug("begin...");
sleep(1.5);
latch.countDown();
log.debug("end...{}", latch.getCount());
}).start();
log.debug("waiting...");
latch.await();
log.debug("wait end...");
}
CountdownLatch还可以配合线程池使用!
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
ExecutorService service = Executors.newFixedThreadPool(4);
service.submit(() -> {
log.debug("begin...");
sleep(1);
latch.countDown();
log.debug("end...{}", latch.getCount());
});
service.submit(() -> {
log.debug("begin...");
sleep(1.5);
latch.countDown();
log.debug("end...{}", latch.getCount());
});
service.submit(() -> {
log.debug("begin...");
sleep(2);
latch.countDown();
log.debug("end...{}", latch.getCount());
});
service.submit(()->{
try {
log.debug("waiting...");
latch.await();
log.debug("wait end...");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
三、CyclicBarrier
CyclicBarrier可以用来辅助线程同步协作,通过它可以实现让一组线程相互等待至某个状态之后再全部同时执行。构造时设置『计数个数』,每个线程执行到某个需要“同步”的时刻调用 await() 方法进行等待,当等待的线程数满足『计数个数』时,继续向下执行!它要做的事情是,让一组线程到达某个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。
CyclicBarrier cb = new CyclicBarrier(2); // 个数为2时才会继续执行
new Thread(()->{
System.out.println("线程1开始.."+new Date());
try {
cb.await(); // 当个数不足时,等待
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("线程1继续向下运行..."+new Date());
}).start();
new Thread(()->{
System.out.println("线程2开始.."+new Date());
try { Thread.sleep(2000); } catch (InterruptedException e) { }
try {
cb.await(); // 2 秒后,线程个数够2,继续运行
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("线程2继续向下运行..."+new Date());
}).start();
注意 CyclicBarrier 与 CountDownLatch 的主要区别在于 CyclicBarrier 是可以重用的 CyclicBarrier 可以被比喻为『人满发车』
四、Semaphore、CountdownLatch和CyclicBarrier区别
区别 | Semaphore | CountdownLatch | CyclicBarrier |
---|---|---|---|
可重用次数 | 单次 | 单次 | 循环使用(多次) |
线程阻塞 | 超过许可数,线程会阻塞 | 单个线程被阻塞,以等待其它线程执行完毕 | 多个线程之间相互阻塞等待 |
使用场景 | 数据库连接池 | 计数器 | 拆分任务的并行计算 |
五、总结
CountDownLatch 和 CyclicBarrier 都能够实现线程之间的等待,只不过它们侧重点不同;CountDownLatch 一般用于某个线程等待其它若干个其他线程执行完任务之后才继续执行的状况;而 CyclicBarrier 一般用于一组线程互相等待至某个状态再同时执行的情况;CountDownLatch 是不能够重用的,而 CyclicBarrier 是可以重用的。