Fork me on GitHub

Two-Phase Termination模式

⭐分俩段终止

🐶简介

⛺正常处理的线程状态为操作中,在停止该线程时,线程会先开始打扫工作,从操作中变为 终止处理中。在终止处理中 线程不会突然终止,而是会

st=>start: 开始
e=>end: 终止
op1=>operation: 操作中
op2=>operation: 终止处理中
st->op1->op2->e

🐯该模式的主要特点如下:
. 安全的终止线程(安全性)
. 必定会终止线程(生存性)
. 发出终止指令后尽快进行终止处理(响应性)

🐉实例程序

main类
public class Main {
    public static void main(String[] args) {
        System.out.println("main: BEGIN");
        try {
            // 启动线程
            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) {
            e.printStackTrace();
        }
        System.out.println("main: END");
    }
}
CountThread类

. shudownRequest方法无需设置为synchronized,终止标记时一旦被设置为true后就不会再变为false的闭锁。

public class CountupThread extends Thread {
    // 计数值
    private long counter = 0;

    // 发出终止请求后变为true
    private volatile boolean shutdownRequested = false;

    // 终止请求
    public void shutdownRequest() {
        shutdownRequested = true;
        // 确保线程在sleep和wait时也会被终止
        interrupt();
    }

    // 检查是否发出了终止请求
    public boolean isShutdownRequested() {
        return shutdownRequested;
    }

    // 线程体
    public final void run() {
        try {
            while (!isShutdownRequested()) {
                doWork();
            }
        } catch (InterruptedException e) {
        } 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);
    }
}

🐱登场角色

TerminationRequest(终止请求发出者)

Terminator发送终止请求

Terminator(终止者)
  • 接受终止请求,并执行终止处理,shutdownRequest方法不需要synchronized。
  • Terminator带有一个表示自己是否已经接受终止请求的标志(闩)。
  • 可能会编写teminator 的子类,doWorkdoShutDown设为protext方法。

🐰拓展思路

  • 不能使用Thread.stop方法,处于访问临界区的代码也会终止,无法保证实例安全性
  • 仅仅检查标志是不够的,线程在sleep、wait时必须调用interrupt方法对线程下达中断指示。
  • 仅仅检查中断状态也是不够的,即捕获InterruptException,使用isInterrupted方法来检查线程是否处于终止状态。但是只要线程有一处忽略InterruptException,上面方法就行不通。
  • 在长时间处理前检查终止请求
  • join方法和isAlive方法,
    • join等待指定线程终止
    • isAlive确定指定线程是否已经终止。
  • java.util.concurrent.ExecutorService接口和Two-Phase Termination模式
    • isShutdown方法用于确认shutwown方法是否已经被调用。
    • isTerminated方法用于确认线程是否已经实际停止。
  • 要捕获程序整体的终止时
    • 未捕获异常的处理器
      • 使用Thread.setDefaultUncaughtExceptionHandler静态方法,可以设置未捕获的异常的处理器,设置了未捕获异常的处理器后,程序将不会输出调用堆栈而是直接退出。
    • 退出钩子
      • 退出钩子是指java虚拟机推出时启动的线程,java虚拟机退出是指System.exit()被调用或是全部非守护线程终止时。
      • 可以使用java.lang.Runtime的实例方法addShutdownHook方法来编写程序完全终止时的终止处理。
      • 示例程序
                    public class Main {
            public static void main(String[] args) {
                System.out.println("main:BEGIN");
        
                // (1) 设置未捕获的异常的处理器
                Thread.setDefaultUncaughtExceptionHandler(
                    new Thread.UncaughtExceptionHandler() {
                        public void uncaughtException(Thread thread, Throwable exception) {
                            System.out.println("****");
                            System.out.println("UncaughtExceptionHandler:BEGIN");
                            System.out.println("currentThread = " + Thread.currentThread());
                            System.out.println("thread = " + thread);
                            System.out.println("exception = " + exception);
                            System.out.println("UncaughtExceptionHandler:END");
                        }
                    }
                );
        
                // (2) 设置退出钩子
                Runtime.getRuntime().addShutdownHook(
                    new Thread() {
                        public void run() {
                            System.out.println("****");
                            System.out.println("shutdown hook:BEGIN");
                            System.out.println("currentThread = " + Thread.currentThread());
                            System.out.println("shutdown hook:END");
                        }
                    }
                );
        
                // (3) 大约 3 秒后启动执行“整数除零计算”的线程
                new Thread("MyThread") {
                    public void run() {
                        System.out.println("MyThread:BEGIN");
                        System.out.println("MyThread:SLEEP...");
        
                        try {
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                        }
        
                        System.out.println("MyThread:DIVIDE");
        
                        // 「整数除零计算」
                        int x = 1 / 0;
        
                        // 不会来到这里
                        System.out.println("MyThread:END");
                    }
                }.start();
        
                System.out.println("main:END");
            }
        }
        
        

🐷延申阅读

java.util.concurrent.CountDownLatch类

使用java.util.concurrent.CountDownLatch类可以实现等待指定次数countDown方法被调用这一功能。

java.util.concurrent.CyclicBarrier类

  • CyclicBarrier可以周期性的创建屏障,在屏障解除之前,碰到屏障的线程无法继续前进,当指定个数的线程到达屏障时,屏障就会被解除。
  • 在创建CyclicBarrier的示例时,可以指定Runnable对象,这个对象被称为屏障操作,每次屏障解除后,都会执行该操作。

📖参考书籍

  • 图解Java多线程设计模式

☀️诗与远方

你无法察觉你脸上淌下的泪水,而我也无法替你逝去。

posted @ 2022-03-21 23:41  bugMaker-java  阅读(37)  评论(0编辑  收藏  举报