前言
CountDownLatch适用场景
- 一个主任务由多个子任务组成
- 主任务需要等待子任务完成才能继续执行
在正常情况下,主线程是不会等待子线程的
- 代码
class CountDownLatchT2 {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
try {
Thread.sleep(2000);//线程睡眠
System.out.println("hello world");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
IntStream.range(0, 4).forEach(i -> new Thread(runnable).start());//启动4个线程
System.out.println("子线程启动完毕");
System.out.println("主线程执行完毕");
}
}
复制代码
使用CountDownLatch
public class CountDownLatchT {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(4);//创建CountDownLatch实例,参数一般为子线程数量
Runnable runnable = ()-> {
try {
Thread.sleep(2000);
System.out.println("hello world");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
countDownLatch.countDown();//子线程运行完线程数减1直到 0
}
};
IntStream.range(0,4).forEach(i ->new Thread(runnable).start());
System.out.println("子线程启动完毕");
countDownLatch.await();//主线程等待
System.out.println("主线程执行完毕");
}
}
复制代码
CountDownLatch大致流程和原理
代码含义
CountDownLatch源码
- 一种同步辅助,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。
CountDownLatch
使用给定的计数进行初始化。 由于调用了countDown
方法,await
方法会阻塞直到当前计数达到零,然后释放所有等待线程,并且任何后续的await
调用都会立即返回。 这是一种一次性现象——计数无法重置。 如果您需要重置计数的版本,请考虑使用CyclicBarrier
。
await()方法
- 使当前线程等待直到闩锁(latch)倒计时为零,除非线程被中断。
- 如果当前计数为零,则此方法立即返回。
- 如果当前计数大于零,则当前线程出于线程调度目的而被禁用并处于休眠状态,直到发生以下两种情况之一:
- 由于调用了countDown方法,计数达到零; 或者
- 一些其他线程中断当前线程。
- 如果当前线程:
- 在进入此方法时设置其中断状态; 或者
- 等待时被打断,
然后抛出InterruptedException并清除当前线程的中断状态。
countDown()方法
- 递减锁存器的计数,如果计数达到零,则释放所有等待的线程。
- 如果当前计数大于零,则递减。 如果新计数为零,则为线程调度目的重新启用所有等待线程。
- 如果当前计数为零,则什么也不会发生。
countDown()
大致原理如下:
await(long timeout, TimeUnit unit)方法
- 使当前线程等待直到闩锁倒计时为零,除非线程被中断,或者指定的等待时间已过。
- 如果当前计数为零,则此方法立即返回值为true 。
- 如果当前计数大于零,则当前线程出于线程调度目的而被禁用并处于休眠状态,直到发生以下三种情况之一:
- 由于调用了countDown方法,计数达到零; 或者
- 其他一些线程中断当前线程; 或者
- 指定的等待时间已过。
- 如果计数达到零,则该方法返回值为true 。
- 如果当前线程:
- 在进入此方法时设置其中断状态; 或者
- 等待时被打断,
然后抛出InterruptedException并清除当前线程的中断状态。
- 如果指定的等待时间过去,则返回值false 。 如果时间小于或等于零,则该方法根本不会等待。
参数:
timeout – 等待的最长时间
unit – timeout参数的时间单位
返回值:
如果计数达到零,则为true如果在计数达到零之前经过了等待时间,则为false
该方法可以防止主线程(主任务)无限等待
样例(出现无限等待的情况)
public class CountDownLatchT {
public static void main(String[] args) throws InterruptedException {
//创建CountDownLatch实例,参数一般为子线程数量
CountDownLatch countDownLatch = new CountDownLatch(5);
Runnable runnable = ()-> {
try {
Thread.sleep(2000);
System.out.println("hello world");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
countDownLatch.countDown();//子线程运行完线程数 -1直到 0
}
};
IntStream.range(0,4).forEach(i ->new Thread(runnable).start());
System.out.println("子线程启动完毕");
countDownLatch.await();//主线程等待
System.out.println("主线程执行完毕");
}
}
复制代码
样例(使用await(long timeout, TimeUnit unit)
)
public class CountDownLatchT {
public static void main(String[] args) throws InterruptedException {
//创建CountDownLatch实例,参数一般为子线程数量
CountDownLatch countDownLatch = new CountDownLatch(5);
Runnable runnable = ()-> {
try {
Thread.sleep(2000);
System.out.println("hello world");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
countDownLatch.countDown();//子线程运行完线程数 -1直到 0
}
};
IntStream.range(0,4).forEach(i ->new Thread(runnable).start());
System.out.println("子线程启动完毕");
countDownLatch.await(2001, TimeUnit.MILLISECONDS);//主线程等待
System.out.println("主线程执行完毕");
}
}
复制代码
主线程就不会无限等待了
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END