java异步转同步CountDownLatch
在我们使用java异步编程的过程中,如果碰上需要异步转同步的场景,就可以使用Java 1.5后给我们提供的类:CountDownLatch
一、作用
1.主线程等待子线程执行完毕后再开始执行
2.程序启动过程中等待所有线程初始化操作完成后再执行后面的操作
二、怎么使用
//CountDownLatch使用很简单,只有一个Int有参构造,所以创建的时候必须指定一个int数字,下面称之为N
CountDownLatch count = new CountDownLatch(2);
注意:N的值必须 >=0,小于0会出现异常:java.lang.IllegalArgumentException: count < 0
其次,如果N==0,程序不会报错,但这个CountDownLatch对象没有意义,即无法进行异步转同步,因为计数器本身就是0,不需要递减为0
三、核心方法
//调用该方法后,当前线程等待,需要其他线程调用countDown()方法后,才能继续执行
count.await();
//和上面的await()方法对应,同时跟计数器N大小对应,当主线程调用await()方法后,子线程需要调用N次countDown()后,主线程才会开始执行,这个N就是new CountDownLatch(N)这里的N。
count.countDown();
//重载方法,功能跟await()类似,但是可以指定一个时间,达到这个时间后,无论子线程有没有执行countDown方法,主线程都会结束等待,开始执行。如果子进程提前执行了countDown方法,则主线程会提前开始运行,而不需要等时间结束
count.await(2000, TimeUnit.MILLISECONDS);
参数1:时间跨度
参数2:时间单位,这里是毫秒,即2s后,无论子线程有没有执行countDown(),主线程都会开始执行
四、案例一:主线程等待两个子线程执行结束
public class demo1 {
public static void main(String[] args) throws InterruptedException {
//创建一个CountDownLatch对象,计数器N为2,需要执行两次countDown()
CountDownLatch count = new CountDownLatch(2);
//创建线程1,休眠3s后执行count.countDown()方法,唤醒主线程继续执行
new Thread(() -> {
try {
//休眠3秒
Thread.sleep(3000);
} catch (InterruptedException e) {
}
System.out.println("子线程1初始化结束,countDown减一");
count.countDown();//计数器减一,减到0时,主线程开始执行
}).start();
//创建线程2,休眠3s后执行count.countDown()方法,唤醒主线程继续执行
new Thread(() -> {
try {
//休眠3秒
Thread.sleep(3000);
} catch (InterruptedException e) {
}
System.out.println("子线程2初始化结束,countDown减一");
count.countDown();//计数器减一,减到0时,主线程开始执行
}).start();
System.out.println("主线程开始等待");
count.await();//调用该方法后,主线程开始等待,等待子线程执行countDown()
//子线程调用countDown()方法后主线程开始执行
System.out.println("主线程开始执行,程序初始化成功");
}
}
运行结果:主线程开始等待,等待两个子线程结束后再开始执行
可以看出来,运行结果有两种,一种是线程1先结束,一种是线程2先结束,但无论哪种情况,都需要两个子线程都结束后才会执行主线程,由此可以看出countDownLatch只能保证主线程等待,不能保证子线程的执行顺序,只要最后子线程都执行结束并调用countDown()方法,主线程就能继续执行。
案例二、一个子线程执行两次countDown方法
public class demo1 {
public static void main(String[] args) throws InterruptedException {
//创建一个CountDownLatch对象,计数器N为2,需要执行两次countDown()
CountDownLatch count = new CountDownLatch(2);
//创建线程1,休眠3s后执行count.countDown()方法,唤醒主线程继续执行
new Thread(() -> {
try {
//休眠3秒
Thread.sleep(3000);
} catch (InterruptedException e) {
}
System.out.println("子线程1初始化结束,countDown减2");
count.countDown();//计数器减一,减到0时,主线程开始执行
count.countDown();//计数器减一,减到0时,主线程开始执行
}).start();
System.out.println("主线程开始等待");
count.await();//调用该方法后,主线程开始等待,等待子线程执行countDown()
//子线程调用countDown()方法后主线程开始执行
System.out.println("主线程开始执行,程序初始化成功");
}
}
结果:尽管计数器N=2,而只有一条子线程,但是依然可以唤醒主线程,由此可知,N≠线程数量,N=countDown()方法执行次数。无论有几条线程,只要最终执行的countDown()方法次数=N,主线程都能被唤醒。
案例三、主线程定时唤醒
public class demo1 {
public static void main(String[] args) throws InterruptedException {
//创建一个CountDownLatch对象,计数器N为2,需要执行两次countDown()
CountDownLatch count = new CountDownLatch(2);
//创建线程1,休眠3s后执行count.countDown()方法,唤醒主线程继续执行
new Thread(() -> {
try {
//休眠3秒
Thread.sleep(3000);
} catch (InterruptedException e) {
}
System.out.println("子线程1初始化结束,countDown减2");
count.countDown();//计数器减一,减到0时,主线程开始执行
count.countDown();//计数器减一,减到0时,主线程开始执行
}).start();
System.out.println("主线程开始等待");
//2s后无论子线程有没有执行countDown(),主线程都会开始执行
count.await(2000,TimeUnit.MILLISECONDS);
//子线程调用countDown()方法后主线程开始执行
System.out.println("主线程开始执行,程序初始化成功");
}
}
结果:由于子线程会休眠3s,而主线程只等待2s,所以在子线程执行结束之前,主线程就被唤醒了,然后子线程才执行结束。
结论:
CountDownLatch count = new CountDownLatch(N);
1.调用count.await()方法后,无论哪个线程,几个线程,只要执行N次count.countDown()方法,都能唤醒等待线程。
2. count.await(2000,TimeUnit.MILLISECONDS);
2s后无论其他线程有没有执行countDown(),等待线程都会开始执行,2s内执行完countDown(),则会提前唤醒等待线程。
【end】
如果觉得文章对你有帮助的话,可以微信关注我的公众号:码农小诚
关注我的公众号更多技术文章分享实时更新,与你共同成长,回复【555】还有55本java技术书籍,免费送。