标题 | 链接 |
---|---|
Semaphore | Java并发编程之Semaphore. |
一、概念
CountDownLatch简单理解为一个线程间的倒计时工具,可以使一个线程A在等待另外的一些子线程(B、C、D…)完成各自的工作后,A再进行工作。每当一个子线程执行完毕时,计数器就会减一,当计数器减为0时,主线程A再开始执行。
二、用法
1、一等多
某一个线程开始阻塞,直至其他n个线程执行完毕,再唤醒。
常用于多线程批量计算,多个线程计算的结果集统一插入数据库(计算过程中要考虑所用工具是否有并发问题)。
//初始化一个为n的计数器
countDownLatch cdl = new CountDownLatch(n);
//主线程等待
cdl.await();
//每个线程执行完毕时,计数器就减1
cdl.countDown();//-1
cdl.countDown();//-1
cdl.countDown();//-1
...
//当计数器变为0时,即减了n次,在await()的线程就会被唤醒
2、多等一
强调的是多个线程在同一时刻开始执行,多await(),一countDown()。
//初始化一个计数器为1的对象
CountDownLatch cdl = new CountDownLatch(1);
//多个线程进行等待
cdl.await();
cdl.await();
cdl.await();
...
//主线程减1,唤醒在等待中的线程继续执行。
cdl.countDown();
三、示例
1、一等多
/**
* @author chengyanqi
* @date 2020/7/13 22:34
*/
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch cdl = new CountDownLatch(5);//计数器
for (int i = 0; i < 5; i++) {
int finalI = i;
Runnable runnable = () -> {
System.out.println("子线程:" + Thread.currentThread().getName() + "开始执行");
try {
Thread.sleep(finalI * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cdl.countDown();//-1
System.out.println("子线程:" + Thread.currentThread().getName() + "执行完毕,当前计数器中的个数:" + cdl.getCount());
};
Thread thread = new Thread(runnable, "Thread" + i);
thread.start();
}
System.out.println("主线程进入等待,等待子线程执行完毕...");
cdl.await();//等待
System.out.println("所有线程执行完毕,主线程开始执行...当前计数器中的个数:" + cdl.getCount());
}
}
子线程:Thread0开始执行
主线程进入等待,等待子线程执行完毕...
子线程:Thread2开始执行
子线程:Thread4开始执行
子线程:Thread3开始执行
子线程:Thread1开始执行
子线程:Thread0执行完毕,当前计数器中的个数:4
子线程:Thread1执行完毕,当前计数器中的个数:3
子线程:Thread2执行完毕,当前计数器中的个数:2
子线程:Thread3执行完毕,当前计数器中的个数:1
子线程:Thread4执行完毕,当前计数器中的个数:0
所有线程执行完毕,主线程开始执行...当前计数器中的个数:0
2、多等一
/**
* @author chengyanqi
* @date 2020/7/13 22:47
*/
public class CountDownLatchDemo2 {
public static void main(String[] args) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(5);
CountDownLatch countDownLatch = new CountDownLatch(1);
for (int i = 0; i < 5; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "准备就绪");
countDownLatch.await();//所有线程等待中
System.out.println(Thread.currentThread().getName() + "开始执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
new Thread(runnable, "Thread" + i).start();//开启线程
}
Thread.sleep(1000);
System.out.println("所有线程准备就绪");
for (int i = 5; i > 0; i--) {
System.out.println(i + "秒后开始执行...");
Thread.sleep(1000);
}
countDownLatch.countDown();//-1
System.out.println("所有线程开始执行!");
}
}
Thread0准备就绪
Thread2准备就绪
Thread3准备就绪
Thread1准备就绪
Thread4准备就绪
所有线程准备就绪
5秒后开始执行...
4秒后开始执行...
3秒后开始执行...
2秒后开始执行...
1秒后开始执行...
所有线程开始执行!
Thread0开始执行
Thread3开始执行
Thread2开始执行
Thread4开始执行
Thread1开始执行