CountDownLatch
一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。
问题:老师组织一次考试,做完题目的学生就可以交卷,然后可以去做自己的事情。老师则要等所有的学生交完试卷后才能整理试卷。
下面使用CountDownLatch来模拟上面的问题。
- 老师类:负责在全部学生交卷后整理试卷
/**
* 老师
* @author sky
*
*/
public class Teacher implements Runnable {
@Override
public void run() {
System.out.println("老师整理试卷");
}
}
2. 学生类:做题,做完后交卷,之后自由活动
import java.util.concurrent.CountDownLatch;
public class Student implements Runnable{
/**
* 考试学生名称
*/
private String name;
/**
* 接下来要做的事
*/
private String nextThing;
/**
* 做题时间
*/
private int time ;
private CountDownLatch latch;
public Student(String n,String nt,CountDownLatch c,int t){
this.name = n;
this.nextThing = nt;
this.latch = c;
this.time = t;
}
@Override
public void run() {
System.out.println(name+" 考试中");
try {
Thread.sleep(time*1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name+" 交卷了,花费时间:"+time+"秒");
latch.countDown();
System.out.println(name+" 去"+nextThing);
}
}
3.考试类
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Exam {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
int sNum = 3;
CountDownLatch latch = new CountDownLatch(sNum);
ExecutorService service = Executors.newFixedThreadPool(sNum);
String [] names ={"A","B","C"};
String [] nextThing = {"吃饭","打魔兽","看xiaoy解说"};
System.out.println("开始考试了");
Student student;
for (int i = 0; i < sNum; i++) {
student = new Student(names[i], nextThing[i], latch,i+1);
service.submit(student);
}
latch.await();
service.submit(new Teacher());
service.shutdown();
}
}
4.输出:
开始考试了
B 考试中
A 考试中
C 考试中
A 交卷了,花费时间:1秒
A 去吃饭
B 交卷了,花费时间:2秒
B 去打魔兽
C 交卷了,花费时间:3秒
C 去看xiaoy解说
老师整理试卷
CyclicBarrier
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。
CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。
接着用上面考试的问题,但是有一点不同,学生在昨晚试卷后不能马上交卷,要等所有学生都做完后才能一起交卷。
使用CyclicBarrier实现方式:
1. 老师类:负责在全部学生交卷后整理试卷(无变化)
public class Teacher implements Runnable {
@Override
public void run() {
System.out.println("老师整理试卷");
}
}
2. 学生类:做题,等待所有学生都做完题目后一起交卷,之后自由活动
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Student implements Runnable{
/**
* 考试学生名称
*/
private String name;
/**
* 接下来要做的事
*/
private String nextThing;
/**
* 做题需要时间
*/
private int time;
private CyclicBarrier barrier;
public Student(String n,String nt,CyclicBarrier c,int t){
this.name = n;
this.nextThing = nt;
this.barrier = c;
this.time=t;
}
@Override
public void run() {
System.out.println(name+" 考试中");
try {
Thread.sleep(time*1000L);
System.out.println(name+" 交卷了,花费时间:"+time+"秒");
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(name+" 去"+nextThing);
}
}
3. 考试类
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Exam {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
int sNum = 3;
CyclicBarrier barrier = new CyclicBarrier(sNum,new Teacher());
ExecutorService service = Executors.newFixedThreadPool(sNum);
String [] names ={"A","B","C"};
String [] nextThing = {"吃饭","打魔兽","看xiaoy解说"};
System.out.println("开始考试了");
Student student;
for (int i = 0; i < sNum; i++) {
student = new Student(names[i], nextThing[i], barrier,i+1);
service.submit(student);
}
service.shutdown();
}
}
4. 输出
开始考试了
A 考试中
C 考试中
B 考试中
A 交卷了,花费时间:1秒
B 交卷了,花费时间:2秒
C 交卷了,花费时间:3秒
老师整理试卷
A 去吃饭
B 去打魔兽
C 去看xiaoy解说