等待唤醒机制
在java.util.concurrent包下 有许多并发工具来帮我们以简单的方式实现多线程的各种机制
今天我们来看CountDownLatch和CyclicBarrier,他们都能实现使线程间等待和唤醒机制。
CountDownLanch
先来看CountDownLatch,它最主要的方法是countDown(),和await()方法。CountDownLatch允许定义一个数量 表示要等待的线程数。假如有这么一个情景,某个线程需要等待执行某些任务的5个线程执行完毕后在执行。此时可以使用CountDownLatch的构造器传入需要等待的线程数。
final CountDownLatch latch = new CountDownLatch(5);
然后启动五个线程
for (int i = 0; i < 5; i++) { Thread th = new Thread(new Task(latch )); th.start(); }
在每个线程执行完任务后调用latch.countDown()
需要等待的线程需要调用lanch.await(),然后将需要等待的线程和五个线程同时启动你会看到,5个线程是先执行的
实例:
package thread; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * 假设有三个同学 老师必须等到三个同学都完成作业才开始检查 */ public class CountDownLanchTest { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(10); final CountDownLatch latch = new CountDownLatch(3); Student s1 = new Student(latch); Student s2 = new Student(latch); Student s3 = new Student(latch); Thread teacher = new Thread(new Runnable() { @Override public void run() { try { latch.await(); } catch (InterruptedException interruptedException) { interruptedException.printStackTrace(); } System.out.println("老师检查"); } }); executorService.execute(teacher); executorService.execute(s1); executorService.execute(s2); executorService.execute(s3); executorService.shutdown(); } } class Student implements Runnable { private CountDownLatch latch; public Student(CountDownLatch latch) { this.latch = latch; } @Override public void run() { System.out.println(Thread.currentThread().getName()+ " begin do home work"); try { //模拟做作业时间 TimeUnit.MILLISECONDS.sleep(3000); } catch (InterruptedException interruptedException) { interruptedException.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "已完成"); //完成作业 执行 latch.countDown(); } }
执行结果:
pool-1-thread-2 begin do home work
pool-1-thread-4 begin do home work
pool-1-thread-3 begin do home work
pool-1-thread-2已完成
pool-1-thread-3已完成
pool-1-thread-4已完成
老师检查
CyclicBarrier
再说cyclicBarrier,其实他们俩有一点相似,CountDownLatch实现的是等待某一部分任务执行完毕,最后执行等待的任务。不同的是cyclicBarrier实现需要执行任务的一组线程相互等待,直至这些线程均已就绪才开始执行任务。发现了吗 前者是等待任务执行完毕,才执行等待任务,后者是等待执行任务的线程全部就绪,才一起开始执行任务。此外这个cyclicBarrier是可以重置的,本篇暂时不详细解释。并且cyclicBarrier的构造方法可以传入一个线程,此线程会在其它任务线程均已就绪后执行。
来看示例:
package thread; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * 线程间相互等待 cyclicBarrier可复用 * 类似跑步比赛 各位选手全部就位后 裁判才会打响发令枪 */ public class CycleBarrierTest { public static void main(String[] args) { //定义需要等待的线程数,以及所有线程就绪后执行的任务 CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() { @Override public void run() { System.out.println("5个线程就位"); } }); for (int i = 0; i < 5; i++) { Thread th = new Thread(new Task(cyclicBarrier)); th.start(); } } } class Task implements Runnable{ private CyclicBarrier cyclicBarrier; public Task(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { System.out.println(Thread.currentThread().getName() + " 已就位"); try { cyclicBarrier.await(); } catch (InterruptedException interruptedException) { interruptedException.printStackTrace(); } catch (BrokenBarrierException e) { //e.printStackTrace(); System.out.println(cyclicBarrier.isBroken()); } //任务 System.out.println(Thread.currentThread().getName() + " is running"); } }
执行结果:
Thread-0 已就位
Thread-4 已就位
Thread-2 已就位
Thread-1 已就位
Thread-3 已就位
5个线程就位
Thread-2 is running
Thread-4 is running
Thread-0 is running
Thread-3 is running
Thread-1 is running
假如理解了上面两个简单的例子,对这个两个类的使用应该就有一些谱了,其实看那么多概念不如马上动手实践一下,能更好的理解
总结:
CountLanch和CycleBarrier都可以实现线程等待,CountLanch 一般用于一个线程等待其它线程执行完毕,CycleBarrier用于一组线程等待至某个状态后一起执行;CycleBarrier可以重复使用CountLanch不行