前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多线程设计模式解读3—Two-phase Termination(两阶段终止)模式

多线程设计模式解读3—Two-phase Termination(两阶段终止)模式

作者头像
java达人
发布2018-10-08 11:21:17
8280
发布2018-10-08 11:21:17
举报
文章被收录于专栏:java达人

有时候,我们希望提前结束线程,但安全可靠地停止线程,并不是一件容易的事情,如果立即停止线程,会使共享的数据结构处于不一致的状态,如目前已经废弃使用的Thread类的stop方法(它会使线程在抛出java.lang.ThreadDeath之后终止线程,即使是在执行synchronized方法的时候)。更好的做法是执行完终止处理,再终止线程,即Two-phase Termination,两阶段终止模式。

该模式有两个角色:

Terminator,终止者,负责接收终止请求,执行终止处理,处理完成后再终止自己。

TerminationRequester:终止请求发出者,用来向Terminator发出终止请求。

该模式示例代码如下:

Terminator:

代码语言:javascript
复制
public class TerminateTestThread extends Thread {
    // 计算值
    private long num = 0;

    // 是否关闭标志
    private volatile boolean isShutdown = false;

    // 终止请求
    public void terminate() {
        isShutdown = true;
        interrupt();
    }

    // 检查关闭状态
    public boolean isShutdown() {
        return isShutdown;
    }

    @Override
    public void run() {
        try {
            while (!isShutdown()) {
                doWork();
            }
        } catch (InterruptedException e) {
        } finally {
            doShutdown();
        }
    }

    // 操作
    private void doWork() throws InterruptedException {
        num++;
        System.out.println("num:  = " + num);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
        }
    }

    // 终止处理
    private void doShutdown() {
        System.out.println("doShutdown: Save result");
        System.out.println("doShutdown result: num = " + num);
        System.out.println("doShutdown: Save END");
    }
}

TerminationRequester:

代码语言:javascript
复制
public class TerminationRequesterMain {
    public static void main(String[] args) {
        try {
            // 启动线程
            TerminateTestThread t = new TerminateTestThread();
            t.start();

            //等待一段时间
            Thread.sleep(1000);

            // 发送线程的终止请求
            t.terminate();

            // 等待线程终止
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

这段代码可以看出实现两阶段终止模式必须注意的是:

使用线程停止标志和interrupt方法,两者缺一不可

代码语言:javascript
复制
public void terminate() {
        isShutdown = true;
        interrupt();
    }

这里使用了isShutdown作为线程停止标志,变量采用volatile修饰,避免了使用显式锁的开销,又保证了内存可见性。线程run方法会检查isShutdown属性,如果属性为true,就停止线程,但线程可能调用了阻塞方法,处于wait状态,任务也就可能永远不会检查isShutdown标志;线程也有可能处于sleep()状态,等sleep时间过后再执行终止状态,程序的响应性就下降了。你可以把方法改成如下运行,线程停止明显变慢了许多:

代码语言:javascript
复制
public void terminate() {
        isShutdown = true;
  }

因此,需要调用interrupt()方法,发出中断线程的请求。那么,但为什么不直接在run方法中使用isInterrupted方法检查线程是否处于中断状态呢,如下面代码:

代码语言:javascript
复制
@Override
    public void run() {
        try {
            while (!isInterrupted()) {
                doWork();
            }
        } catch (InterruptedException e) {
        } finally {
            doShutdown();
        }
    }

运行后发现线程始终没有停止,这是因为在doWork方法内部,sleep方法抛出InterruptedException异常后中断状态被清除,捕获时也没有作出任何处理,需要Thread.currentThread().interrupt()保留线程中断状态,我们把方法稍微改动即可:

代码语言:javascript
复制
// 操作
    private void doWork() throws InterruptedException {
        num++;
        System.out.println("num:  = " + num);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

因此,同时使用线程停止标志和interrupt方法,其实是给线程停止操作上了双重保险,开发人员或许会忽略InterruptedExceptio异常,或许线程处于wait或者长时间的sleep的状态,这些情况都要提前考虑好。

以上是一个简单的Two-phase Termination(两阶段终止模式)范例,在复杂实现中,我们可能还要考虑其他方面的内容,如如何停止处于生产者-消费者模式中的线程,停止顺序是怎样的,在停止时如何处理队列中的待处理任务;如果有多个可停止线程,那么线程停止标志怎样实现共享,减少锁的使用。我们需要一套可复用的解决方案,来综合考虑这些问题。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-08-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java达人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档