CountDownLatch,英文翻译为倒计时锁存器,是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
CountDownLatch有一个正数计数器,countDown()方法对计数器做减操作,await()方法等待计数器达到0。所有await的线程都会阻塞直到计数器为0或者等待线程中断或者超时。
流程如下:
TW线程调用计数器,线程就此阻塞,其他线程调减计数器的方法,待到TW定义的计数器都减完后,TW继续往下执行。
Demo:
public class DemoThread {
static CountDownLatch countDownLatch=new CountDownLatch(3);
public static void main(String[] args) throws InterruptedException, ExecutionException {
System.out.println("开始=============");
new Thread(){
@Override
public void run() {
System.out.println("线程1");
countDownLatch.countDown();
}
}.start();
new Thread(){
@Override
public void run() {
System.out.println("线程2");
try{
sleep(2000);
}catch (Exception e){
}
countDownLatch.countDown();
}
}.start();
new Thread(){
@Override
public void run() {
System.out.println("线程3");
//减少计数器
countDownLatch.countDown();
}
}.start();
for(int i=0;i<10;i++){
System.out.println(i);
if(i==5){
//调用await()方法,阻塞主线程,等到其他线程减数器减为0,继续往下执行
countDownLatch.await();
}
}
}
}
CountDownLatch是阻塞主线程,等到计数器减少为0,主线程继续往下执行。CyclicBarrier也会定义计数器,但是这个计数器是阻塞计数器,即哪个线程调用计数器,就阻塞哪个线程,等计数器调用完后,阻塞的线程一起往下执行,示意图如下:
Demo:
public static void main(String[] args) throws InterruptedException, ExecutionException {
CyclicBarrier cyclicBarrier=new CyclicBarrier(3);
new Thread(){
@Override
public void run() {
System.out.println("线程1");
try {
//阻塞计数器,阻塞线程,等待计数器为0往下执行线程
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("线程1往下执行");
}
}.start();
new Thread(){
@Override
public void run() {
System.out.println("线程2");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("线程2往下执行");
}
}.start();
new Thread(){
@Override
public void run() {
System.out.println("线程3");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("线程3往下执行");
}
}.start();
System.out.println("主线程执行完毕");
}
Semaphore 通常我们叫它信号量, 可以用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。
常用方法和说明:
acquire(): 获取一个令牌,在获取到令牌、或者被其他线程调用中断之前线程一直处于阻塞状态。
acquire(int permits): 获取一个令牌,在获取到令牌、或者被其他线程调用中断、或超时之前线程一直处于阻塞状态。
acquireUninterruptibly(): 获取一个令牌,在获取到令牌之前线程一直处于阻塞状态(忽略中断)。
tryAcquire(): 尝试获得令牌,返回获取令牌成功或失败,不阻塞线程。
tryAcquire(long timeout, TimeUnit unit) 尝试获得令牌,在超时时间内循环尝试获取,直到尝试获取成功或超时返回,不阻塞线程。release() : 释放一个令牌,唤醒一个获取令牌不成功的阻塞线程。 hasQueuedThreads() 等待队列里是否还存在等待线程。
getQueueLength(): 获取等待队列里阻塞的线程数。
drainPermits(): 清空令牌把可用令牌数置为0,返回清空令牌的数量。 availablePermits(): 返回可用的令牌数量。
应用场景
通常用于那些资源有明确访问数量限制的场景,常用于限流 。
比如:数据库连接池,同时进行连接的线程有数量限制,连接不能超过一定的数量,当连接达到了限制数量后,后面的线程只能排队等前面的线程释放了数据库连接才能获得数据库连接。
Demo:
1.停车场容纳总停车量10。
2、当一辆车进入停车场后,显示牌的剩余车位数响应的减1.
3、每有一辆车驶出停车场后,显示牌的剩余车位数响应的加1。
4、停车场剩余车位不足时,车辆只能在外面等待。
代码如下:
//停车场同时容纳的车辆10
private static Semaphore semaphore=new Semaphore(10);
public static void main(String[] args) {
//模拟100辆车进入停车场
for(int i=0;i<100;i++){
Thread thread=new Thread(new Runnable() {
public void run() {
try {
System.out.println("===="+Thread.currentThread().getName()+"来到停车场");
if(semaphore.availablePermits()==0){
System.out.println("车位不足,请耐心等待");
}
semaphore.acquire();//获取令牌尝试进入停车场
System.out.println(Thread.currentThread().getName()+"成功进入停车场");
Thread.sleep(new Random().nextInt(10000));//模拟车辆在停车场停留的时间
System.out.println(Thread.currentThread().getName()+"驶出停车场");
semaphore.release();//释放令牌,腾出停车场车位
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},i+"号车");
thread.start();
}
}