Two-Phase Termination
分两阶段终止的意思,它是一种先执行完终止处理再终止线程的模式
先从“操作中” 状态变为“终止处理中”状态,然后再真正的终止线程,这就是Two-Phase Termination模式
该模式的要点如下:
安全地终止线程(安全性)
必定会终止处理(生存性)
发出终止请求会尽快进行终止处理(响应性)
程序实例:
CountupThread 表示进行计数的线程的类
Main 测试程序行为的类
CountupThread类:
counter字段表示当前的计数值
shutdownRequested字段是表示是否已发出终止请求(用于判断线程是否要进入“终止处理中”状态)其中volatile关键字是防止线程脏读
shutdownRequest()方法,当要终止CountupThread线程会调用
interrupt()方法,为了确保线程在sleep()和wait()状态也会被终止
isShutdownRequested()表示检查是否发生了终止请求
doWork()进行模拟实际操作的方法
doShutdown()执行终止处理的方法(这里只显示counter)
public class CountupThread extends Thread{
//计数值
private long counter = 0;
//发出终止请求后变为true
private volatile boolean shutdownRequested =false;
//终止请求
public void shutdownRequest(){
shutdownRequested = true;
interrupt();
}
//检查是否发生了终止请求
public boolean isShutdownRequested(){
return shutdownRequested;
}
//线程体
public final void run(){
try {
while(!isShutdownRequested()){
doWork();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
doShutdown();
}
}
//操作
private void doWork() throws InterruptedException{
counter++;
System.out.println("doWork:counter = "+counter);
Thread.sleep(500);
}
private void doShutdown(){
System.out.println("doShutdown:counter = "+counter);
}
}
Main类:
启动线程,然后再10秒后终止该线程
public class Main {
public static void main(String[] args) {
try {
System.out.println("main:begin");
//启动线程
CountupThread t = new CountupThread();
t.start();
Thread.sleep(10000);
//线程的终止请求
System.out.println("main: shutdownRequest");
t.shutdownRequest();
System.out.println("main : join");
//等待线程终止
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("main End");
}
}
扩展
ExecutorService接口与Two-Phase Termination模式
用于确认终止处理已执行到哪个阶段的方法
isShutdown 方法用于确认shutdown方法是否已经被调用的方法
isTerminated方法用于确认线程是否已经实际停止的方法
|
isShutdown方法 |
isTerminated方法 |
操作中 |
false |
false |
终止处理中 |
true |
false |
终止 |
true |
true |
java.util.concurrent.CountDownLatch类
java.util.concurrent.CyclicBarrier类
CountDownLatch类可以实现“等待指定次数的CountDown方法被调用”
下面看看关于CountDownLatch的实例:
程序实现了让线程处理10 项MyTask工作并等待10项工作都处理完成的工作
MyTask类:
调用doTask执行“实际处理”
调用countDown处理MyTask的run方法的数值减1,当所有的MyTask处理后,计数值将会为0,主线程会从await方法中返回。
public class MyTask implements Runnable{
private final CountDownLatch doneLatch;
private final int context;
private static final Random random = new Random(314159);
public MyTask(CountDownLatch doneLatch,int context){
this.doneLatch = doneLatch;
this.context = context;
}
@Override
public void run() {
// TODO Auto-generated method stub
doTask();
doneLatch.countDown();
}
protected void doTask(){
String name = Thread.currentThread().getName();
System.out.println(name+":MyTask:Begin:context="+context);
try {
Thread.sleep(random.nextInt(3000));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
System.out.println(name+":MyTask:End:context="+context);
}
}
}
Main类:
准备工作对象ExecutorService
创建CountDownLatch的实例,初始值为10
调用execute方法执行10个MyTask
调用await方法等待doneLatch的计数为0
调用shutdown方法终止service
public class Main {
private static final int TASKS = 10;//工作的个数
public static void main(String[] args) {
System.out.println("main begin");
ExecutorService service = Executors.newFixedThreadPool(5);
CountDownLatch doneLatch = new CountDownLatch(TASKS);
try {
for (int t = 0; t < TASKS; t++) {
service.execute(new MyTask(doneLatch, t));
}
System.out.println("await");
doneLatch.await();
} catch (InterruptedException e) {
}finally{
service.shutdown();
System.out.println("main end");
}
}
}
java.util.concurrent.CyclicBarrier类
当想多次重复进行线程同步的时候,使用CyclicBarrier会比较方便
CyclicBarrier可以周期性(cyclic)地创建出屏障barrier,在屏障未解除之前,碰到屏障的线程是无法继续前进的,只有当指定的个数的线程到达屏障处后,屏障才会被解除。
源码实例:
MyTask3:
调用doPhase(phase)方法进行第phase阶段的工作
调用await方法表示自己已经完成了第phase阶段的工作
public class MyTask2 implements Runnable{
private static final int PAHSE = 5;
private final CyclicBarrier phaseBarrier;
private final CountDownLatch doneLatch;
private final int context;
private static final Random random = new Random(314159);
public MyTask2(CyclicBarrier phaseBarrier,CountDownLatch doneLatch,int context){
this.phaseBarrier = phaseBarrier;
this.doneLatch = doneLatch;
this.context = context;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
for(int phase = 0;phase<PAHSE;phase++){
doPhase(phase);
//在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
phaseBarrier.await();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
protected void doPhase(int phase){
String name = Thread.currentThread().getName();
System.out.println(name+":MyTask2:Begin:context= "+context+" ,phase ="+phase);
try {
Thread.sleep(random.nextInt(3000));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
System.out.println(name+":MyTask2:end:context= "+context+" ,phase ="+phase);
}
}
}
Main类
public class Main2 {
private static final int Threads = 3;
public static void main(String[] args) {
System.out.println("begin");
//由ExecutorService提供进行工作的线程
ExecutorService service = Executors.newFixedThreadPool(Threads);
//屏障被解除时的操作
Runnable barrierAction = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Barrier Action!");
}
};
//CyclicBarrier用于使线程步调一致
CyclicBarrier phaseBarrier = new CyclicBarrier(Threads,barrierAction);
//用于确认工作是否结束
CountDownLatch doneLatch = new CountDownLatch(Threads);
try {
for(int t= 0;t<Threads;t++){
service.execute(new MyTask3(phaseBarrier, doneLatch, t));
}
//等待工作结束
System.out.println("await");
//能够阻塞线程 直到调用Threads次doneLatch.countDown() 方法才释放线程
doneLatch.await();
} catch (InterruptedException e) {
}finally{
service.shutdown();
System.out.println("end");
}
}
}
总结
“线程优雅地执行终止处理,然后终止运行”
三点提现:
1. 安全地终止 (即使接收到终止请求,线程也不会立即终止)
2.必定会进行终止处理(线程在接收到终止请求后,会中断可以中断的wait,转入终止处理)
3.发出终止请求后会尽快进入终止处理(线程在接收到终止请求后,会中断可以中断的sleep,尽快进入终止处理)
这就是Two-Phase Termination模式