由于blog各种垃圾评论太多,而且本人审核评论周期较长,所以懒得管理评论了,就把评论功能关闭,有问题可以直接qq骚扰我

JAVA 线程中断机制

JAVA 西门飞冰 3503℃
[隐藏]

1.什么是中断机制

首先:

一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止

所以,Thread.stop, Thread.suspend, Thread.resume 都已经被废弃了。

其次:

在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。

因此,Java提供了一种用于停止线程的协商机制——中断,也即中断标识协商机制。

中断只是一种协作协商机制,Java没有给中断增加任何语法,中断的过程完全需要程序员自己实现。它与 stop 最大的区别是:stop 是由系统强制终止线程,而线程中断则是给目标线程发送一个中断信号,如果目标线程没有接收线程中断的信号并结束线程,线程则不会终止,具体是否退出或者执行其他逻辑由目标线程决定。

实际案例:顾客在无烟餐厅中吸烟,服务员希望他别吸烟了,不是强行停止他吸烟,而是给他的标志位打为true,具体的停止吸烟还是要顾客自己停止。(体现了协商机制)

2.中断的相关方法

我们来看下线程中断最重要的 3 个方法,它们都是来自 Thread 类!

image-20221208214628237

1、public void interrupt():仅仅是设置线程的中断状态为true,发起一个协商而不会立刻停止线程

2、public static boolean interrupted():判断线程是否被中断,并清除当前中断状态这个方法做了两件事

(1)返回当前线程的中断状态,

(2)将当前线程的中断状态设为false

3、public boolean isInterrupted():通过检查中断标志位,判断当前线程是否被中断

3.使用中断标识停止线程

中断机制就是在需要中断的线程中 不断监听中断状态,一旦发生中断,就执行相应的中断处理业务逻辑stop线程,下面列出三种实现方法。

3.1.通过volatile变量实现

volatile保证了可见性,t2线程修改了标志位后能马上被t1线程看到

public class interruptDemo {
    // 设置中断标识
    static volatile boolean isStop = false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            // 通过while循环不断监听中断状态
            while (true){
                // 如果这个标志位被其他线程改为true了
                if (isStop) {
                    System.out.println(Thread.currentThread().getName()+"\t isStop被修改为true,程序终止");
                    break;
                }
                // 如果没停止,那就一直打印
                System.out.println("t1 ------hello volatile");
            }
        },"t1").start();

        TimeUnit.MILLISECONDS.sleep(20);

        // 在短暂的暂停之后,设置中断标识为true
        new Thread(()->{
            isStop = true;
        },"t2").start();
    }
}

执行结果:

t1 ------hello volatile
t1 ------hello volatile
t1 ------hello volatile
t1 ------hello volatile
t1 ------hello volatile
t1	 isStop被修改为true,程序终止

3.2.通过AtomicBoolean实现

public class interruptDemo {

    // 设置中断标识
    static AtomicBoolean atomicBoolean = new AtomicBoolean(false);

    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            // 通过while循环不断监听中断状态
            while(true){
                // 如果这个标志位被其他线程改为true了
                if(atomicBoolean.get()){
                    System.out.println(Thread.currentThread().getName()+"\t isStop被修改为true,程序终止");
                    break;
                }
                // 如果没停止,那就一直打印
                System.out.println("t1 ------hello AtomicBoolean");
            }
        },"t1").start();

        TimeUnit.MILLISECONDS.sleep(20);

        new Thread(()->{
            atomicBoolean.set(true);
        },"t2").start();
    }
}

执行结果:

t1 ------hello AtomicBoolean
t1 ------hello AtomicBoolean
t1 ------hello AtomicBoolean
t1 ------hello AtomicBoolean
t1	 isStop被修改为true,程序终止

3.3.通过Thread类自带的中断api实现

默认的中断标志位是false,然后通过Thread类自带的interrupt方法被改为了true

public class interruptDemo {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true) {
                // 通过while循环不断监听中断状态
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println(Thread.currentThread().getName() + "\t isInterrupted()被修改为true,程序终止");
                    break;
                }
                // 如果没停止,那就一直打印
                System.out.println("t1 ------hello interrupt ");
            }
        }, "t1");
        t1.start();

        TimeUnit.MILLISECONDS.sleep(20);

        new Thread(()->{
            // 把t1中断
            t1.interrupt();
        },"t2").start();
    }
}

执行结果:

t1 ------hello interrupt 
t1 ------hello interrupt 
t1 ------hello interrupt 
t1 ------hello interrupt 
t1 ------hello interrupt 
t1	 isInterrupted()被修改为true,程序终止

4.API源码分析

实例方法interrupt(),没有返回值:仅仅是设置一个中断标识位:

image-20221122203956516

调用底层的C语言

image-20221122204036687

实例方法isInterrupted,返回布尔值:

public boolean isInterrupted() {
    return interrupted;
}
private volatile boolean interrupted;	 //也调用了c底层

5.中断阻塞案例

在我们基本中断程序的骨架上 + 一个sleep阻塞,看看会出现什么情况。

public class interruptDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true)
            {
                if(Thread.currentThread().isInterrupted())
                {
                    System.out.println(Thread.currentThread().getName()+"\t " +
                            "中断标志位:"+Thread.currentThread().isInterrupted()+" 程序停止");
                    break;
                }

                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    //加了这个,程序可以终止,只会爆异常
                    // Thread.currentThread().interrupt();
                    e.printStackTrace();
                }

                System.out.println("-----hello InterruptDemo");
            }
        }, "t1");
        t1.start();

        //暂停几秒钟线程
        TimeUnit.SECONDS.sleep(1);

        new Thread(() -> t1.interrupt(),"t2").start();
    }
}

执行结果:

爆异常了,并且程序一直在跑

-----hello InterruptDemo
-----hello InterruptDemo
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at com.fblinux.JUC.interruptDemo.lambda$main$0(interruptDemo.java:20)
	at java.base/java.lang.Thread.run(Thread.java:833)
-----hello InterruptDemo
-----hello InterruptDemo

加了Thread.currentThread().interrupt();

-----hello InterruptDemo
-----hello InterruptDemo
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at com.fblinux.JUC.interruptDemo.lambda$main$0(interruptDemo.java:19)
	at java.base/java.lang.Thread.run(Thread.java:833)
-----hello InterruptDemo
t1	 中断标志位:true 程序停止

原因说明:

1、中断标志位,默认false

2、线程t2 向线程t1发出了中断协商,t2调用t1.interrupt(),中断标志位true

3、中断标志位true,正常情况,程序停止,^_^

4、中断标志位true,异常情况,InterruptedException,将会把中断状态将被清除,并且将收到InterruptedException 。中断标志位false,导致无限循环

5、在catch块中,需要再次给中断标志位设置为true,2次调用停止程序才OK

6.总结

具体来说,当对一个线程调用interrupt()时:

1、如果线程处于正常活动状态,那么会将该线程的中断状态标志设置为true,仅此而已

被设置中断标志的线程将继续正常运行,不受影响。

所以,interrupt()并不能真正的中断线程,需要被调用的线程自己进行配合才行。

2、如果线程处于被阻塞状态(例如处于sleep、wait、join等状态),在别的线程中调用当前线程对象的interrupt方法,那么线程将立即退出被阻塞状态,并抛出一个interruptedException异常。

 

转载请注明:西门飞冰的博客 » JAVA 线程中断机制

喜欢 (5)or分享 (0)