【Java并发编程】 interrupt 方法详解

【Java并发编程】 interrupt 方法详解

1.介绍

程序中,有些线程的终端需要外部干预,比如有线程存在while(true)循环,或者一些阻塞操作,比如sleepwaitjoin等。

终端线程的方式,如果直接使用 stopsuspend等烦啊,对程序来说是不太严谨的,这些方法类似于直接杀死线程,可能会造成数据问题。

为了更好的解决这些问题,我们可以使用interrupt方法,调用该方法就相当于告诉线程你要被打断了,赶快准备结束你的线程,具体什么时候结束,你自己决定,我只是给你传个话。这种方式就比stop这种强行让你结束好多了。

2.打断阻塞状态线程

sleep,wait,join这几个方法都会让线程进入阻塞状态,该状态下,如果线程被打断,则会清空打断状态(被打断时为true),并抛出异常。以sleep为例:

public class Main {
    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");
        t1.start();
        Thread.sleep(500);
        t1.interrupt();
      System.out.println(t1.isInterrupted());
    }
}

结果:打断标记为false,本来被打断后打断标记应该为true,但是因为当阻塞线程被打断后会抛出异常,并且清楚打断标记。 为啥会这样呢?

一个正在阻塞的线程被打断,因为该线程还在阻塞,不知道到底发生了什么,突然被打断就会抛异常来停止当前的阻塞状态,以应对外面发生的变化。也就是响应中断。

3.打断正常状态的线程

public class Main {
    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            while (true) {
                boolean interrupted = Thread.currentThread().isInterrupted();
                if (interrupted) {
                 	System.out.println("被打断");
                    break;
                }
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");
        t1.start();
        Thread.sleep(500);
        t1.interrupt();
      System.out.println(t1.isInterrupted());
    }
}

我们通过运行结果可以发现,打断正常线程是打断标记会置为true,然后线程可以继续执行,所以就给了打断线程处理后事的时间,自己选择何时结束线程。这样做的目的是 对之前的线程的数据保存现场或者处理,如果直接中断就会出现很多问题,比如一个线程拿着锁(手动上锁下锁)突然被中断,那么该锁就一直不会被释放,其他线程就会一直等待,造成死锁。

4.两阶段终止模式

提出问题

如果在一个线程中“体面”的终止另一个线程,也就是给终止线程一个处理后事的机会。

错误的解决思路

1.使用stop方法终止线程

这种思路是错误的 因为Thread.stop()方法在结束线程时,会直接终止线程,并且会立即释放这个线程所有的锁,而这些锁恰恰是用来维持对象一致性的。如果此时,写入线程写入的数据正写到一半,并强行终止,那么对象就会被写坏,同时由于锁已经释放,另外一个等待该锁的线程就会顺理成章的对到了这个不一致的对象。

2.使用System.exit(int)方法终止线程

这个方法会让整个程序都停止,而我们的目的仅仅是停止一个线程

正确的解决思路

用interrupt方法,当陷入阻塞时 中断线程抛出异常 可以处理后事,当中断正常状态线程时 可以根据中断标记 来处理后事

public class TPTInterrupt {
    private Thread thread;

    public void start() {
        thread = new Thread(() -> {
            while (true) {
                Thread current = Thread.currentThread();
                if (current.isInterrupted()) {
                    System.out.println("料理后事");
                    break;
                }
                try {
                    Thread.sleep(1000);
                    System.out.println("保存数据");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // 将打印标记置为true
                    current.interrupt();
                }
                // 执行监控操作
            }
        }, "监控线程");
        thread.start();
    }

    public void stop() {
        thread.interrupt();
    }
}

测试

public class TPTInterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        TPTInterrupt t = new TPTInterrupt();
        t.start();

        Thread.sleep(3500);
        System.out.println("stop");
        t.stop();
    }
}

结果

保存数据
保存数据
保存数据
stop
料理后事
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at org.example.juc.TPTInterrupt.lambda$start$0(TPTInterrupt.java:19)
at java.lang.Thread.run(Thread.java:750)

如果线程正在正常执行,那么如果线程被打断,打断标志就为true,就可以处理后事了,如果线程正在阻塞状态,那么如果线程被打断,就需要再次打断该线程使标志为true,因为阻塞异常被打断会抛出异常并重置为false,这样才能给线程处理后事的机会并且知道线程在阻塞状态被打断了。

image-20230203175308038

5.打断 park 线程

**打断 park 线程,不会清空打断状态 **

public class parkDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println("park...");
            LockSupport.park();
            System.out.println("unpark...");
            System.out.println("打断状态: " + Thread.currentThread().isInterrupted());
        }, "t1");

        t1.start();

        Thread.sleep(500);
        t1.interrupt();
    }
}

结果

park...
unpark...
打断状态: true

如果打断标志已经是 true,则park会失效

public class parkDemo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("park...");
                LockSupport.park();
                System.out.println("打断状态: " + Thread.currentThread().isInterrupted());
            }

        }, "t1");

        t1.start();

        Thread.sleep(500);
        t1.interrupt();
    }
}

结果

park...
打断状态: true
park...
打断状态: true
park...
打断状态: true
park...
打断状态: true
park...
打断状态: true
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值