1. CountDownLatch
CountDownLatch允许一个或多个线程等待其他线程完成操作。简单理解便是阻塞当前线程后不断去监听其他线程的状态,等到其他线程都完成操作后,当前线程才会继续执行,但该类监听粒度又比较细。
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("1");
downLatch.countDown();
System.out.println("2");
downLatch.countDown();
}
}).start();
downLatch.await();
System.out.println("3");
}
private static CountDownLatch downLatch = new CountDownLatch(2);
}
运行结果
构造函数的参数代表监听点的个数,每调用一次countDown,参数值便会-1,直到参数值变成零(可以理解为是一个计数器)。downLatch.await()阻塞该线程直到计数器变成0。这里有个有趣的点,比如监听的点有2个,但构造函数传入的参数>2,此时线程则会一直出于阻塞状态,但我们又不能让线程一直处于阻塞状态,可以使用带时间戳的await方法,await(long time, TineUnit unit),超时便不会阻塞线程。
2. 同步屏障CyclicBarrier
同步屏障CyclicBarrier跟CountDownLatch很类似,但相比CountDownLatch会更加有趣些,可以看成是CountDownLatch的改良版。CyclicBarrier通过设置一个屏幕,让线程到达时被阻塞,直到最后一个线程到达屏障时,屏障才打开,所有被拦截的线程才会继续执行。
public class CyclicBarrierTest {
public static void main(String[] args) throws Exception{
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("1");
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("2");
}
}).start();
System.out.println("3");
barrier.await();
System.out.println("4");
}
private static CyclicBarrier barrier = new CyclicBarrier(2);
}
运行结果
CyclicBarrier监听的是线程而不再是某一个点,相比CountDownLatch,可以使用reset()重置,因此计算错误,也可以重试。getNumberWaiting()获取阻塞的线程数;isBroken()获取阻塞的线程是否被中断。
3. Semaphore
Semaphore(信号量)可以用作流量控制,特别是公共资源有限的场景下,控制指定的线程数能获取到资源,比如:数据库连接池,也可以实现限流等。主要有以下方法。
请求资源:acquire() / tryAcquire()
释放资源:release()
还有一些其他的方法,返回可以的许可数、获取等待的线程数、是否有线程正在等待获取许可数等等,有需要可以直接查看API文档。