并发工具
CountDownLatch(倒计时器)
背景
- 1.5之后被引入的,跟它一起被引入的工具还有CyclicBarrier、Semaphore、concurrentHashMap和BlockingQueue。
- 存在于java.util.current包下
概念
- countDownLatch这个类使用一个线程等待其他线程各自执行完成后再执行
- 是通过计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后计数器就减1,当计数器为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
适用于一个线程需要等待其他多个线程执行结果的应用场景
进程,线程,协程
实现代码
public class CountdownLatchExample {
public static void main(String[] args) throws InterruptedException {
final int totalThread = 10;
CountDownLatch countDownLatch = new CountDownLatch(totalThread);
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(() -> {
int count = totalThread;
while (count > 0) {
try {
Thread.sleep(2000);
countDownLatch.countDown();
// 每个线程在这里将countDownLatch减少
System.out.println("run... " + count--);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
System.out.println("I`m waiting~~");
// 这个主线程在这里等待其他线程把countDownLatch的值减为0,再继续往下执行。
countDownLatch.await();
System.out.println("end..");
executorService.shutdown();
}
}
result:
CyclicBarrier
概念:
循环栅栏。可以循环利用的屏障,让所有线程都等待完成后才继续下一步行动
例子:就像生活中我们会约朋友们到某个餐厅一起吃饭,有些朋友可能会早到,有些朋友可能会晚到,但是这个餐厅规定必须等到所有人到齐之后才会让我们进去。这里的朋友们就是各个线程,餐厅就是 CyclicBarrier。
使用场景:
可以用于多线程计算数据,最后合并计算结果的场景
代码实现(栅栏可以重复利用)
package Interview.多线程.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
/**
* @author chenbin
* @datetime 2020-08-28 11:44
*/
public class CyclicBarrierDemo {
static class TaskThread extends Thread {
CyclicBarrier barrier;
public TaskThread(CyclicBarrier barrier) {
this.barrier = barrier;
}
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(getName() + " 到达栅栏 A");
barrier.await();
System.out.println(getName() + " 冲破栅栏 A");
Thread.sleep(2000);
System.out.println(getName() + " 到达栅栏 B");
barrier.await();
System.out.println(getName() + " 冲破栅栏 B");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
int threadNum = 5;
CyclicBarrier barrier = new CyclicBarrier(threadNum, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 完成最后任务");
}
});
for(int i = 0; i < threadNum; i++) {
new TaskThread(barrier).start();
}
}
}
result
CountDownLatch和CyclicBarrier的比较
- CountDownLatch 是一次性的,CyclicBarrier 是可以循环利用的
- CountDownLatch 参与的线程的职责不一样的,有的在倒计时,有的在等待倒计时结束。CyclicBarrier 参与的线程池职责是一样的。
- CyclicBarrier会在await处阻塞等待,而CountDownLatch在await处不会阻塞等待
- CyclicBarrier提供了例如isBroken,getNumberWaiting等方法能够查询当前状态,而CountDownLatch提供的方法较少
Semaphore
概念
Semaphore是一个信号量,必须由获取它的线程释放。
常用于限制可以访问某些资源的线程数量,例如通过Semaphore限流。
适用于对特定资源需要控制能够并发访问资源的线程个数。需要先执行acquire方法获取许可证后线程才能往下继续执行,否则只能阻塞等待,使用完后需要用release方法归还许可
代码实现
public class SemaphoreExample {
public static void main(String[] args) {
final int clientCount = 3;
final int totalRequestCount = 10;
Semaphore semaphore = new Semaphore(clientCount);
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < totalRequestCount; i++) {
executorService.execute(() ->{
try{
semaphore.acquire();
System.out.println(semaphore.availablePermits()+" ");
}catch (Exception e){
e.printStackTrace();
}finally {
semaphore.release();
}
});
}
executorService.shutdown();
}
}
result
阻塞在线程内部,同时执行的线程数量不会超过3