CountDownLatch简单用法
- 代码开发中经常遇到这样的场景:某几个任务执行完成后主线程再继续向下执行,其实实现这个功能非常简单下面列出两种方式。
- 代码如下:示例1
import java.util.concurrent.CountDownLatch;
public class CountDownLaunchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLaunchDemo demo = new CountDownLaunchDemo();
//1.原始做法
//demo.test1();
//2.用countdownlatch
demo.test2();
System.out.println("主线程执行完毕");
}
private void test1() throws InterruptedException {
Thread thread1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "做一个耗时任务");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread thread2 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "做一个耗时任务");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
private void test2() throws InterruptedException {
CountDownLatch launch = new CountDownLatch(2);
Thread thread1 = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "做一个耗时任务");
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
} finally {
//减1,最好放到finally里防止异常退出未执行计数器减1操作
launch.countDown();
}
});
Thread thread2 = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "做一个耗时任务");
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
} finally {
//减1,最好放到finally里防止异常退出未执行计数器减1操作
launch.countDown();
}
});
thread1.start();
thread2.start();
//阻塞直到减为0继续向下执行
launch.await();
}
}
test1使用原始方式实现的,test2使用CountDownLatch实现的。test1,test2控制台输出如下:
Thread-1做一个耗时任务
Thread-0做一个耗时任务
主线程执行完毕
CyclicBarrier简单用法
当某几个任务执行完成后再执行其它任务,用CyclicBarrier可以轻松实现。
先看下代码:示例2
public class CycliBarrierDemo {
public static void main(String[] args) throws InterruptedException {
CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {
@Override
public void run() {
System.out.println("两个线程都执行完成");
}
});
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + "执行");
try {
Thread.sleep(2000);
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
});
thread1.start();
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + "执行");
try {
Thread.sleep(4000);
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
});
thread2.start();
thread1.join();
thread2.join();
System.out.println("主线程执行成功!");
}
}
控制台输出:
线程Thread-0执行
线程Thread-1执行
两个线程都执行完成
主线程执行成功!
- 当thread1和thread2都执行完成时会执行回调cyclicBarrier 构造函数传进去的方法,这就能够实现当两个任务执行完成再执行其它任务,如示例2所示。
- 有些人经常把CyclicBarrier与CountDownLatch混淆在一起,实际上两个工具差别很大,就从使用上来说CountDownLatch一般会在主线程用await()方法等待其它线程执行完毕,其它线程如果任务执行完毕会调用用countDown()方法,当计数器为0时,唤醒主线程的await()方法,示例1说明了这一点。
- 还有就是直观上CountDownLatch要调用await()和countDown()方法,而CyclicBarrier只调用await()方法
- 再有CountDownLatch调用countDown直到计数器为0唤醒主线程,计数器不能自动恢复,而CyclicBarrier调用await()后计数器加1操作,直到变为构造函数值(本例为2)后,立即回调构造函数传进去的方法(如示例2所示),并且计数器马上重置为0。下面看下CyclicBarrier自动重置计数器并循环使用,如示例3
示例3
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CycliBarrierDemo {
public static void main(String[] args) throws InterruptedException {
CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new Runnable() {
@Override
public void run() {
System.out.println("两个线程都执行完成");
}
});
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + "执行");
try {
Thread.sleep(2000);
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
});
thread1.start();
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + "执行");
try {
Thread.sleep(4000);
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
});
thread2.start();
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + "执行");
try {
Thread.sleep(2000);
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
});
thread3.start();
Thread thread4 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + "执行");
try {
Thread.sleep(2000);
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
});
thread4.start();
thread1.join();
thread2.join();
thread3.join();
thread4.join();
System.out.println("主线程执行成功!");
}
}
控制台输出:
线程Thread-0执行
线程Thread-1执行
线程Thread-2执行
线程Thread-3执行
两个线程都执行完成
两个线程都执行完成
主线程执行成功!
可以看出控制台输出了两次“两个线程都执行完成”,证明CyclicBarrier可以循环使用。