JUC并发编程基础篇第五章之线程中断状态[你理解的线程中断,只会Thread.interrupted()吗]

发布时间 2023-04-07 15:40:47作者: 爱吃糖的靓仔

1、什么是线程的中断机制

首先 - 线程的中断不应该由其他线程来中断或者停止,而是自己线程中断和停止; 自己决定的命运,所以Thread.stop, Thread.suspend, Thread.resume都废弃了;

其次 - 在java中没法立即停止一个线程,然后停止线程却显得尤为重要

  • java中提供了一种用于停止线程的协商机制 -- 中断, 也即中断标识协商机制
  • 中断只是一种协商机制,java中没有给中断增加任何语法,中断的过程完全由程序员自己实现
  • 若要中断一个线程,你需要手动调用该线程的isterruput方法,该方法也仅仅是把该线程对象的中断标识设置为true
  • 接着你需要自己写代码不断检测当前线程的标识,如果为true,表示别的线程请求这条线程中断,此时做什么,就需要自己写代码了;

每个线程中都有一个中断标识符,用于表示线程是否被中断;该标识符为true 为中断 ,反之 未中断; 通过调用线程对象的interrupt()方法将该线程的标识位设为true; 可以在别的线程中调用, 也可以在自己的线程中调用 通俗解释 : 服务员中断客户的吸烟的操作, 进行提醒而不是直接抢走客户的烟中断这个操作,而是等待客户自己中断吸烟的操作;

2、isterrupt interrupted isInterrupted 区别

方法 含义
isterrupt 实例方法 ; 设置线程的中断状态为true , 发起一个协商而不会立即停止线程
interrupted 静态方法; 判断线程是否被中断 并 清除当前中断状态 (它做了两件事) 1. 返回当前线程的中断状态,测试当前线程是否已经被中断 2. 将当前线程的中断状态清零并且重新设置为false,清除线程的中断状态
isInterrupted 实例方法; 判断当前线程是否被中断 (检查中断标志位) 如果该线程阻塞的时候调用wait(),或者object的join, sleep ,此时他的中断状态会被清除,并且会抛出InterruptedExcepiotn

3、面试题

3.1、 如何停止中断运行中的线程

  • 方法1 通过一个vaoliate变量实现
public class interruputTest {
    private static volatile boolean flag = false;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (!flag) {
                System.out.println(Thread.currentThread().getName()+线程被中断了);
                break;
            }
            System.out.println(Thread.currentThread().getName()+线程被中断了);
        },a);

        t.start();
        Thread.sleep(2000);
        new Thread(()->{
            flag = true;
        },b).start();
    }
}
  • 方法2 通过AtomicBoolean实现
import java.util.concurrent.atomic.AtomicBoolean;

public class interruputAtomicTest {

    private static AtomicBoolean atomicBoolean = new AtomicBoolean(false);

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (atomicBoolean.get()) {
                System.out.println(Thread.currentThread().getName()+线程被中断了);
                break;
            }
            System.out.println(Thread.currentThread().getName()+线程被中断了);
        },a);

        t.start();
        Thread.sleep(2000);
        new Thread(()->{
            atomicBoolean.set(true);
        },b).start();
    }
}]
  • 方法3 通过Thread类自带的中断api实例方法实现
public class interruputApiTest {

    private static volatile boolean flag = false;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getName()+线程被中断了);
                break;
            }
            System.out.println(Thread.currentThread().getName()+线程被中断了);
        },a);

        t.start();
        Thread.sleep(2000);
        new Thread(()->{
              t.interrupt();
        },b).start();
       ## t.interrupt(); 也可以自己中断
    }
}

3.2、当前线程的中断标识符为true,是不是线程就立马停止了

回答: 不会的, 案例证明

public class interruputDemo1 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 300; i++) {
                System.out.println(i);
            }
        }, "t1");
        t1.start();
        System.out.println("t1线程刚开始的线程状态为" + t1.isInterrupted());
        t1.interrupt();
        System.out.println("t1线程执行完interrupt的线程状态为" + t1.isInterrupted());
        Thread.sleep(2000);
        System.out.println("t1线程执行完数据的线程状态为" + t1.isInterrupted());
    }
}

输出结果 t1线程刚开始的线程状态为false .... t1线程执行完interrupt的线程状态为true 137 138 ...... t1线程执行完数据的线程状态为false(执行完了,自己就变成了false)

3.3、如果线程处于被阻塞的状态(列入sleep,wait,join等状态),在别的线程调用当前线程的interrupt()方法,会发生什么?

答案: 那么该方法将立即退出阻塞状态,抛出interruputedException异常

示例证明

  public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 300; i++) {
                System.out.println(i);
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1");
        t1.start();
        System.out.println("t1线程刚开始的线程状态为" + t1.isInterrupted());
        t1.interrupt();
        System.out.println("t1线程执行完interrupt的线程状态为" + t1.isInterrupted());
        Thread.sleep(2000);
        System.out.println("t1线程执行完数据的线程状态为" + t1.isInterrupted());
    }

在这里插入图片描述

从图中可以看出来,因为虽然抛出了InterruptException但是程序并没有因为打断,而停止运行; 如果想要解决需要添加如下的代码

public class InterruputDemo1 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true){
                if (Thread.currentThread().isInterrupted()){
                    System.out.println(Thread.currentThread().getName()+"中断标记为,程序停止");
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("我在输出哦");
            }
        }, "t1");
        t1.start();
        t1.interrupt();
    }
}

解析

  1. 初始中断标志位是false
    2.t1.interrupt() 中断标志位是true
    3.Thread.sleep(100); 被打印,清除中断标志位是false
    4.Thread.currentThread().interrupt(); 需要再次给他中断标志位设置为true才行

3.4、interrupted的特点

        System.out.println(Thread.currentThread().getName()+"--->"+Thread.interrupted());//false
        System.out.println(Thread.currentThread().getName()+"--->"+Thread.interrupted());//false
        System.out.println("1");
        Thread.currentThread().interrupt();
        System.out.println("2");
        System.out.println(Thread.currentThread().getName()+"--->"+Thread.interrupted()); // 第一此打印真实值,然后清空   true
        System.out.println(Thread.currentThread().getName()+"--->"+Thread.interrupted()); //false

4、 isterrupt和interrupted源码分析

1. interrupt()方法

interrupt() 方法用于中断一个线程,其源码如下:

public void interrupt() {
    if (this != Thread.currentThread()) {
        checkAccess();
    }
    synchronized (blockerLock) {
        Interruptible b = blocker;
        if (b != null) {
            interrupt0(); // If the thread is blocked in an interruptible method.
            b.interrupt(this);
            return;
        }
    }
    interrupt0(); // If the thread is not blocked in an interruptible method.
}

  • interrupt()方法会首先判断当前线程是否是调用interrupt()方法的线程,如果不是则会抛出SecurityException异常。
  • 然后,它会获取锁,检查当前线程是否被阻塞,如果当前线程正在阻塞并且是可中断的阻塞操作,则会调用interrupt0()方法中断该阻塞操作;否则,它会仅仅设置中断标志位为true。
  • 需要注意的是,调用interrupt()方法并不会立即中断线程的执行,它仅仅会设置中断标志位为true,线程需要主动检查中断标志位并做出相应的处理才能有效地中断线程的执行。

2. interrupt()方法

interrupted()方法用于检查当前线程的中断状态,并清除中断标志位。其源码如下:

public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}

  • interrupted()方法会返回当前线程的中断状态,并将中断标志位设置为false。需要注意的是,调用interrupted()方法并不会中断线程的执行,它仅仅是检查当前线程的中断状态。
  • 总之,interrupt()方法和interrupted()方法都可以用于中断线程,但是它们的作用有所不同。interrupt()方法可以中断一个线程的阻塞操作,而interrupted()方法则只能检查中断状态并清除中断标志位。在使用中,需要根据具体的情况选择合适的方法来中断线程。