等待唤醒机制
在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不行