一、JUC下的工具类的使用
1.1 CountDownLatch(闭锁)
核心特性:
- 所有调用了await的线程会阻塞至计数器清零。 通过别的线程将计数器减少。
- 因此核心思想是一批调用await的线程等待另一批线程将计数器清零。
三个重要的方法:
- 构造方法,设置计数器:public CountDownLatch(int count)
- 等待的线程调用await被阻塞至计数器为0:public void await()
- 被等待的线程调用countDown将计数器减一:public void countDown()
使用特点:
- 只能用一次,计数器清零后无法再次使用
- 可以响应中断
- 计数器为0后再调用countDown方法不报错
应用场景:
- 一个线程等多个线程完成才继续工作。如将多个线程扔到线程池里后,一个或几个线程等待所有线程执行完后才继续执行。
- 多个线程等待一个线程完成才继续工作。测试场景。将多个线程阻塞在同一位置,被唤醒后可以实现并发调用某逻辑。
源码层面:
- 内部类继承了AQS,实现了共享锁的逻辑。
- tryAcquireShared判断state是否为0
- tryReleaseShared会将state减一
- 构造函数赋予state初始值
例子: 三个线程等待计数器清零
public static void testCountDownLatch() throws InterruptedException {
for(int i = 0;i<3;i++){
new Thread(()->{
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread:"+Thread.currentThread().getName());
},String.valueOf(i)).start();
}
System.out.println("主线程睡眠2秒");
TimeUnit.SECONDS.sleep(2);
System.out.println("主线程唤醒等待线程");
cdl.countDown();
}
主线程睡眠2秒
主线程唤醒等待线程
thread:2
thread:1
thread:0
CountDownLatch源码分析
1.2 CyclicBarrier(循环屏障)
核心特性:
- 所有调用了await的线程会被阻塞,直到调用await的线程达到某个数量才会唤醒所有线程。
- 因此核心思想是线程间相互等待
使用特点:
- 可以重复使用,在线程被唤醒后,可以重设屏障再次调用await进行阻塞。
- 打破屏障后,在重设屏障前,无法再次调用await进行阻塞,会抛出异常。
- 在所有线程未到齐前,调用reset可以打破屏障,释放所有线程,被释放的线程会抛出异常
应用场景:
- 流程类场景,有多段逻辑,必须所有线程执行完相同一段逻辑后才能进入下一段逻辑。
源码层面:
- 内部使用了ReentrantLock和Condition,依靠condition的await进行阻塞。
CyclicBarrier源码分析
1.3 Exchanger(交换器)
看这里介绍
1.4 Semaphore(信号量)
核心特性:
使用特点:
应用场景:
源码层面:
- 内部类继承了AQS,实现了共享锁的逻辑。
- 构造函数设置state,代表可获取的共享锁数量
- acquire获取指定数量锁
- release释放指定数量锁
代码样例 如下代码为例,根据打印结果可以看出同一时间最多有三个线程进入同步逻辑。
public class SemaphoreTest {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(10);
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 10; i++) {
String name = "num-"+i;
executor.execute(()->{
try {
semaphore.acquire();
System.out.println(name);
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
});
}
executor.shutdown();
}
}
Semaphore源码注释
1.5 Phaser
看这里介绍
二、各个类的区别
2.1 CountDownLatch和CyclicBarrier
相同点:
都是一组线程被阻塞,等待某个时机释放。
不同点:
- 被阻塞时等待的线程不同。
- CountDownLatch是一组线程等待另一组线程调用countDown方法直至将计数器清零才会被释放。
- 而CyclicBarrier是一组线程相互等待,直至所有线程都已经调用await方法才会被释放。
- 使用次数不同
- CountDownLatch是一次性的,计数器清零后就没用了。
- CyclicBarrier在所有线程被释放后可以重置,重复利用。
- 线程被唤醒后,调用countDown和await的结果不同。
- CountDownLatch调用countDown无异常
- CyclicBarrier调用await会抛出异常
|