用volatitle实现设计模式---两阶段终止

本文探讨了如何优雅地终止一个正在运行的线程,通过使用中断和volatile变量来确保线程可以进行必要的清理工作并安全退出。文章对比了使用中断和添加volatile变量的方法,以及如何在主线程中触发监控线程的终止。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Two Phase Termination:在一个线程t1中如何优雅终止线程t2?这里的“优雅”指的是给t2一个料理后事的机会。。

有一个监控线程,由于要去执行监控操作,因此它不断运行,每隔一秒钟执行一些监控操作,为了让他优雅退出,采用了中断的方式,比如在主线程中执行一个中断,若监控线程正在正常执行,中断这个监控线程后,它会设置中断标记为真,这时它不会进入catch块,等到下次循环判断时,发现这个中断标记为真,它可以做一些料理后事的工作,并退出这个监控循环。若主线程中断的监控线程正在sleep时,会进入catch块,因为sleep出现异常后,会清楚中断标记,因此需要获取到当前线程再次设置中断标记为真,这样下次循环时,它的中断标记为真,就可以继续执行料理后事,退出监控线程的循环。

这种方式的缺点是:需要时刻关注interruptException,一旦发生异常,需要重新设置中断标记,容易遗漏!若没有设置中断标记,下次判断时会导致这个if块不成立,不能正确执行料理后事,退出while(true)的循环。

之前使用interrupt()实现:

@Slf4j(topic = "c.TwoPharse1")
public class TwoPharse1 {
    private Thread monitorThread;

    public void start(){
        monitorThread=new Thread(()->{
          while(true){
              Thread current=Thread.currentThread();
              //是否被打断
              if(current.isInterrupted()){
                  //被中断了
                  log.debug("料理后事");
                  break;
              }
             //没有被中断
              try {
                  TimeUnit.SECONDS.sleep(1);
                  log.debug("执行监控记录");
              } catch (InterruptedException e) {
                 //因为sleep出现异常后,会清楚中断标记
                  //需要设置中断标记
                  current.interrupt();
              }
          }
        },"monitor");
        monitorThread.start();
    }
    //在主线程中执行中断----停止监控线程
    public void stop(){
        monitorThread.interrupt();
    }
}

现在使用volatitle实现:
在上述方式中添加boolean变量,当stop为真表示监控线程不应该在while(true)中循环了。

@Slf4j(topic = "c.TwoPharse1")
public class TwoPharse1 {
    private Thread monitorThread;
    private boolean stop=false;

    public void start(){
        monitorThread=new Thread(()->{
          while(true){
              Thread current=Thread.currentThread();
              //是否被打断
              if(stop){
                  //被中断了
                  log.debug("料理后事");
                  break;
              }
             //没有被中断
              try {
                  TimeUnit.SECONDS.sleep(1);
                  log.debug("执行监控记录");
              } catch (InterruptedException e) {
                 //因为sleep出现异常后,会清楚中断标记
                  //需要设置中断标记
                  current.interrupt();
              }
          }
        },"monitor");
        monitorThread.start();
    }
    //在主线程中执行中断----停止监控线程
    public void stop(){
     stop=true;
    }
}

分析:一开始,中断标记为假,监控线程一直执行监控,当主线程调用中断方法,中断监控线程的执行后,设置中断标记为真,这时监控线程的while中的if块再判断时,发现中断标记为真,进入if块,料理后事,退出循环。

此时的问题时,因为monitorThread是一个线程,后来调用interrupt()方法的又是另外一个线程,两个线程对一个共享变量修改,让一个线程的修改对另一个线程可见,此时就需要使用volatitle,修饰共享变量,让一个线程对volatitle关键字修饰的变量的修改对另一个线程可见。

private volatitle boolean stop=false;

如果不加volatile,那可能其他线程将stop的值设置成真了,单monitorThread线程读到的值还是为假,那它就没办法正确退出了

还有一个小问题:监控线程正在sleep的过程中,某个线程执行了stop,将中断标记设置为真了,这时监控线程需要等到sleep结束下次进入循环判断时才会发现中断标记为真,退出循环,但是若监控线程的sleep的时间特别长,则需要很长时间才能到下次循环,若想某个线程执行stop方法设置中断标记为真后,监控线程尽快退出循环,则可以在stop()方法设置中断标记后,执行中断方法,中断中正在sleep的监控线程,让它尽快退出监控循环。

public void stop(){
 stop=true;
 monitorThread.interrupt();
}

测试方法:
TwoPharse1 twp=new TwoPharse1();
twp.start();
TimeUnit.SECONDS.sleep(5);

    log.debug("停止监控");
    twp.stop();

代码:

@Slf4j(topic = "c.TwoPharse1")
public class TwoPharse1 {
    public static void main(String[] args) throws InterruptedException {
        //测试方法
        TwoPharse1 twp=new TwoPharse1();
        twp.start();
        TimeUnit.SECONDS.sleep(5);
        log.debug("停止监控");
        twp.stop();
    }
    private Thread monitorThread;
    private boolean stop=false;
    public void start(){
        monitorThread=new Thread(()->{
          while(true){
              Thread current=Thread.currentThread();
              //是否被打断
              if(stop){
                  //被中断了
                  log.debug("料理后事");
                  break;
              }
             //没有被中断
              try {
                  TimeUnit.SECONDS.sleep(1);
                  log.debug("执行监控记录");
              } catch (InterruptedException e) {
              }
          }
        },"monitor");
        monitorThread.start();
    }
    //在主线程中执行中断----停止监控线程
    public void stop(){
     stop=true;
     monitorThread.interrupt();
    }
}

测试结果:
22:01:03.186 [monitor] DEBUG c.TwoPharse1 - 执行监控记录
22:01:04.193 [monitor] DEBUG c.TwoPharse1 - 执行监控记录
22:01:05.198 [monitor] DEBUG c.TwoPharse1 - 执行监控记录
22:01:06.202 [monitor] DEBUG c.TwoPharse1 - 执行监控记录
22:01:07.187 [main] DEBUG c.TwoPharse1 - 停止监控
22:01:07.187 [monitor] DEBUG c.TwoPharse1 - 料理后事

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值