Java并发编程 interrupt()方法

发布时间 2023-06-09 15:57:04作者: k3ruanren

interrupt()用法

打断正常运行的线程

interrrupt()方法可以用来打断正在运行的线程,也可以打断sleep()、wait()、join()情况下的线程,但是这些情况下被打断线程的打断标记不同。

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j
public class InterruptTest {
    public static void main(String[] args) throws InterruptedException {
        //创建一个线程
        Thread t1 = new Thread(() -> {
            //让线程一直死循环运行
            log.debug("t1开始运行.....");
            while(true){

            }
        },"t1");
        //启动线程t1
        t1.start();
        //main主线程一秒后进行打断操作
        TimeUnit.MILLISECONDS.sleep(1000);
        log.debug("interrupt.....");
        t1.interrupt();
    }
}

通过控制台,可以很清晰的看到,main主线程在执行完打断操作后,t1线程并未终止运行,而是继续执行:

但是如果我们查看线程t1的打断标记会发现,t1线程的打断标记确实为true:

Boolean interrupted = Thread.currentThread().isInterrupted();

这是因为main主线程执行interrupt打断操作,只是通知t1线程,我现在要打断你,但是具体的执行操作还得看t1线程,即t1线程收到main主线程的打断通知后,由t1线程自己觉得是继续运行还是被打断,从而让出cpu,这样的好处在于线程t1如果在执行某些很重要的任务,突然被其他线程强制打断可能会造成很严重的后果,这时可以让t1线程自己选择是否停止工作,也可以在停止工作之前做一些料理后事的工作 。

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j
public class InterruptTest {
    public static void main(String[] args) throws InterruptedException {
        //创建一个线程
        Thread t1 = new Thread(() -> {
            //让线程一直死循环运行
            log.debug("t1开始运行.....");
            while(true){
                Boolean interrupted = Thread.currentThread().isInterrupted();
                //选择被打断后的执行操作
                if(interrupted){
                    log.debug("被打断,停止运行...");
                    break;
                }
            }
        },"t1");
        //启动线程t1
        t1.start();
        //main主线程一秒后进行打断操作
        TimeUnit.MILLISECONDS.sleep(1000);
        log.debug("interrupt.....");
        t1.interrupt();
    }
}
14:41:02.906 [t1] DEBUG InterruptTest - t1开始运行.....
14:41:03.908 [main] DEBUG InterruptTest - interrupt.....
14:41:03.908 [t1] DEBUG InterruptTest - 被打断,停止运行...

打断sleep状态的线程

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j
public class InterruptTest {
    public static void main(String[] args) throws InterruptedException {
        //创建一个线程
        Thread t1 = new Thread(() -> {
            //让线程一直死循环运行
            log.debug("t1开始运行.....");
            //t1线程休眠3秒
            try {
                log.debug("t1 is sleeping...");
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"t1");
        //启动线程t1
        t1.start();
        //main主线程一秒后进行打断操作
        TimeUnit.MILLISECONDS.sleep(1000);
        log.debug("interrupt.....");
        t1.interrupt();
        //查看t1线程的打断标记
        Boolean interrupted = t1.isInterrupted();
        log.debug(interrupted.toString());
    }
}

打断sleep状态的线程,会抛出InterruptedException异常,并且打断标记是false而不是true:

14:50:25.532 [t1] DEBUG InterruptTest - t1开始运行.....
14:50:25.536 [t1] DEBUG InterruptTest - t1 is sleeping...
14:50:26.533 [main] DEBUG InterruptTest - interrupt.....
14:50:26.544 [main] DEBUG InterruptTest - false
Exception in thread "t1" java.lang.RuntimeException: java.lang.InterruptedException: sleep interrupted
	at InterruptTest.lambda$main$0(InterruptTest.java:17)
	at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at java.base/java.lang.Thread.sleep(Thread.java:334)
	at java.base/java.util.concurrent.TimeUnit.sleep(TimeUnit.java:446)
	at InterruptTest.lambda$main$0(InterruptTest.java:15)
	... 1 more

Process finished with exit code 0

两阶段终止模式 Two Phase Termination

在线程t1中如何优雅的终止线程t2?

  • 使用stop()方法终止线程,这显然是不好的行为,因为一个线程调用了stop()方法,那么这个线程就被彻底杀死了,如果该线程正在访问一些共享资源并且加了锁,那么stop()之后该线程将再也不能释放锁,其他线程也就无法访问那些共享资源了。
  • 使用System.exit()方法,这也是错误的选择,这会终止整个程序!

正确的做法是采用两阶段终止模式,具体流程如下图所示:

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j
public class TwoPhaseTest {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination tpt = new TwoPhaseTermination();
        tpt.start();
        TimeUnit.SECONDS.sleep(4);
        tpt.stop();
    }
}

/**
 * 监控类
 */
@Slf4j
class TwoPhaseTermination{
    //监控线程
    private Thread monitor;
    //线程启动方法
    public void start(){
        monitor = new Thread(()->{
            while(true){
                Thread thread = Thread.currentThread();
                Boolean interrupted = thread.isInterrupted();
                if(interrupted){
                    log.debug("料理后事.....");
                    break;
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                    //每隔一秒钟执行监控任务
                    log.debug("监控中....");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //如果在sleep状态被打断,那么中断标记为false,需要手动置为true
                    thread.interrupt();
                }
        }});
        //启动线程
        monitor.start();
    }
    //线程终止方法
    public void stop(){
        //打断线程
        monitor.interrupt();
    }
}
15:38:11.046 [Thread-0] DEBUG TwoPhaseTermination - 监控中....
15:38:12.054 [Thread-0] DEBUG TwoPhaseTermination - 监控中....
15:38:13.060 [Thread-0] DEBUG TwoPhaseTermination - 监控中....
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at java.base/java.lang.Thread.sleep(Thread.java:334)
	at java.base/java.util.concurrent.TimeUnit.sleep(TimeUnit.java:446)
	at TwoPhaseTermination.lambda$start$0(TwoPhaseTest.java:33)
	at java.base/java.lang.Thread.run(Thread.java:829)
15:38:14.014 [Thread-0] DEBUG TwoPhaseTermination - 料理后事.....

Process finished with exit code 0

打断park()线程

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

@Slf4j
public class ParkTest {
    public static void main(String[] args) throws InterruptedException {
        test();

    }

    private static void test() throws InterruptedException {
        Thread t1 = new Thread(()->{
           log.debug("park......");
           LockSupport.park();//线程在此卡住,不继续向下执行
           log.debug("unpark.......");
           log.debug("打断状态:{}",Thread.currentThread().isInterrupted());
        },"t1");
        t1.start();
        //主线程main休眠2秒
        TimeUnit.SECONDS.sleep(2);
        //打断t1线程
        t1.interrupt();
    }

}
15:51:24.581 [t1] DEBUG ParkTest - park......
15:51:26.582 [t1] DEBUG ParkTest - unpark.......
15:51:26.584 [t1] DEBUG ParkTest - 打断状态:true