多线程设计模式

129 阅读2分钟

Two-phase Termination(两阶段终止)模式

Q:如何在一个线程中安全的终止另一个线程?

A:使用线程提供的.stop()方法.

如果这时关闭的线程锁住了共享资源,在线程抛出java.lang.ThreadDeath之后终止线程后,即使是在执行synchronized方法的时候,被杀死后就再也没有机会释放锁, 其它线程将永远无法获取锁。
更好的做法是执行完终止处理,再终止线程,即Two-phase Termination,两阶段终止模式

该模式有两个角色:

Terminator: 接收终止指令,进行实际的终止处理。

TerminationRequester: 发起终止指令。

将终止过程分成两个阶段,其中第一个阶段主要是发送终止指令,而第二阶段则是响应终止指令;气利用java线程中断机制的 interrupt()方法,可以让线程从休眠状态转换到RUNNABLE状态。RUNNABLE状态转换到终止状态,优雅的让线程自己执行完run()方法,所以一般采用的方法是设置一个终止标志,然后线程会在合适的时机检查这个终止标志,如果发现符合终止条件,则自动退出 run()方法。
public class ThreadTest {
    public static void main(String[] args) {
        try {
            // 启动线程
            TerminateTest t = new TerminateTest();
            t.start();
            // 执行一段时间
            Thread.sleep(5000);
            // 发起线程的终止命令
            t.terminate();
            // 等待线程终止
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static class TerminateTest extends Thread {
        // 打螺丝
        private long num = 0;

        // 关闭标志
        private volatile boolean shutdownFlag = false;

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

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

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

        // 模拟操作
        private void doWork() throws InterruptedException {
            num++;
            System.out.println(StrUtil.format("工作中: 已打了{}个螺丝.", num));
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }

        // 终止处理
        private void doShutdown() {
            System.out.println("");
            System.out.println("不让干了,就这样吧");
            System.out.println(StrUtil.format("法外狂徒今天只打了{}个螺丝.", num));
        }
    }
}

执行结果如下: image.png