35两阶段终止模式

如何优雅地终止线程

interrupt()方法和线程终止的标志位

  • 捕获异常重新设置标志位
  • 自定义标志位,避免第三方库处理中指
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    class Proxy {
    boolean started = false;
    //采集线程
    Thread rptThread;

    //启动采集功能
    synchronized void start() {
    //不允许同时启动多个采集线程
    if (started) {
    return;
    }
    started = true;
    rptThread = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) {
    //省略采集、回传实现
    report();
    //每隔两秒钟采集、回传⼀次数据
    try {
    Thread.sleep(2000);
    } catch (InterruptedException e) {
    //重新设置线程中断状态
    Thread.currentThread().interrupt();
    }
    }
    //执⾏到此处说明线程⻢上终⽌
    started = false;
    });
    rptThread.start();
    }

    //终⽌采集功能
    synchronized void stop() {
    rptThread.interrupt();
    }
    }
    上面示例代码能够解决问题,但是建议工作中谨慎使用。原因在于可能在线程的run()方法中调用第三方类库的方法,没有办法保证第三方类库正确处理了线程的中断异常(例如第三方类库在捕获到Thread.sleep()方法抛出的中断异常后,没有重新设置线程的中断状态,那么就会导致线程不能够正常终止。)
    强烈建议设置自己的线程终止标志位,如下代码中,使用isTerminated作为线程终止标志位,此时无论是否正确处理了线程的中断异常,都不会影响线程优雅地终止。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    class Proxy {
    //线程终⽌标志位
    volatile boolean terminated = false;
    boolean started = false;
    //采集线程
    Thread rptThread;

    //启动采集功能
    synchronized void start() {
    //不允许同时启动多个采集线程
    if (started) {
    return;
    }
    started = true;
    terminated = false;
    rptThread = new Thread(() -> {
    while (!terminated) {
    //省略采集、回传实现
    report();
    //每隔两秒钟采集、回传⼀次数据
    try {
    Thread.sleep(2000);
    } catch (InterruptedException e) {
    //重新设置线程中断状态
    Thread.currentThread().interrupt();
    }
    }
    //执⾏到此处说明线程⻢上终⽌
    started = false;
    });
    rptThread.start();
    }

    //终⽌采集功能
    synchronized void stop() {
    //设置中断标志位
    terminated = true;
    //中断线程rptThread
    rptThread.interrupt();
    }
    }

如何优雅地终止线程池

shutdown()和shutdownNow()

  • shutdown
    拒绝新任务,等待线程池中正在执行的任务和已进入阻塞队列的任务执行完后关闭线程池
  • shutdownNow
    拒绝接收新的任务,中断线程池中正在执行的任务,已进入阻塞队列的任务也被剥夺执行机会,不过这些被剥夺执行机会的任务会作为shutdownNow()方法的返回值返回。
    如果需要优雅地结束,需要正确处理线程中断。