线程的中断 - morris131/morris-book GitHub Wiki

线程的中断

java为我们提供了一种调用interrupt()方法来请求终止线程的方法,每一个线程都有一个boolean类型标志,用来表明当前线程是否请求中断,当一个线程调用interrupt()方法时,线程的中断标志将被设置为true。我们可以通过调用Thread.currentThread().isInterrupted()或者Thread.interrupted()来检测线程的中断标志是否被置位。这两个方法的区别是

Thread.currentThread().isInterrupted(): 对象方法 不清除中断标志位 Thread.interrupted(): 静态方法 清除中断标志位(设置为false)

所以说调用线程的interrupt() 方法不会中断一个正在运行的线程,这个机制只是设置了一个线程中断标志位,如果在程序中你不检测线程中断标志位,那么即使设置了中断标志位为true,线程也一样照常运行。

一般来说中断线程分为三种情况:

  1. 中断非阻塞线程
  2. 中断阻塞线程
  3. 不可中断线程

中断非阻塞线程

采用线程共享变量

这种方式比较简单可行,需要注意的一点是共享变量必须设置为volatile,这样才能保证修改后其他线程立即可见。

package com.morris.ch1;

public class InterruptNonBlockWithShareVariable extends Thread {

    // 设置线程共享变量
    volatile boolean isStop = false;

    @Override
    public void run() {
        while(!isStop) {
            long beginTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + " is running");
            // 当前线程每隔一秒钟检测一次线程共享变量是否得到通知
            while (System.currentTimeMillis() - beginTime < 1000) {}
        }
        if (isStop) {
            System.out.println(Thread.currentThread().getName() + " is interrupted");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        InterruptNonBlockWithShareVariable thread = new InterruptNonBlockWithShareVariable();
        thread.start();

        Thread.sleep(5000);
        thread.isStop = true;

    }
}

运行结果如下:

Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is interrupted

采用中断机制

package com.morris.ch1;

public class InterruptNonBlockWithInterrupt extends Thread {

    @Override
    public void run() {
        while(!Thread.currentThread().interrupted()) {
            long beginTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + " is running");
            // 当前线程每隔一秒钟检测一次线程共享变量是否得到通知
            while (System.currentTimeMillis() - beginTime < 1000) {}
        }
        if (!Thread.currentThread().interrupted()) {
            System.out.println(Thread.currentThread().getName() + " is interrupted");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        InterruptNonBlockWithInterrupt thread = new InterruptNonBlockWithInterrupt();
        thread.start();

        Thread.sleep(5000);
        thread.interrupt();

    }
}

运行结果如下:

Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is interrupted

中断阻塞线程

当线程调用Thread.sleep()、Thread.join()、object.wait()再或者调用阻塞的i/o操作方法时,都会使得当前线程进入阻塞状态。那么此时如果在线程处于阻塞状态是调用interrupt() 方法设置线程中断标志位时会抛出一个异常,并且会清除线程中断标志位(设置为false)。这样一来线程就能退出阻塞状态。当然抛出异常的方法就是造成线程处于阻塞状态的Thread.sleep()、Thread.join()、object.wait()这些方法。

package com.morris.ch1;

public class InterruptBlockThread extends Thread {

    @Override
    public void run() {
        // 这里调用的是非清除中断标志位的isInterrupted方法
        while(!Thread.currentThread().interrupted()) {
            System.out.println(Thread.currentThread().getName() + " is running");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // 由于调用sleep()方法清除状态标志位 所以这里需要再次重置中断标志位 否则线程会继续运行下去
                Thread.currentThread().interrupt();
            }
        }
        if (!Thread.currentThread().interrupted()) {
            System.out.println(Thread.currentThread().getName() + " is interrupted");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        InterruptBlockThread thread = new InterruptBlockThread();
        thread.start();

        Thread.sleep(5000);
        thread.interrupt();

    }
}

不可中断线程

有一种情况是线程不能被中断的,就是调用synchronized关键字和reentrantLock.lock()获取锁的过程。但是如果调用带超时的tryLock方法reentrantLock.tryLock(longtimeout, TimeUnit unit),那么如果线程在等待时被中断,将抛出一个InterruptedException异常,这是一个非常有用的特性,因为它允许程序打破死锁。你也可以调用reentrantLock.lockInterruptibly()方法,它就相当于一个超时设为无限的tryLock方法。

package com.morris.ch1;

public class NotInterruptThread {

    public void deathLock(Object lock1, Object lock2) {
        try {
            synchronized (lock1) {
                System.out.println(Thread.currentThread().getName()+ " is running");
                // 让另外一个线程获得另一个锁
                Thread.sleep(10);
                // 造成死锁
                synchronized (lock2) {
                    System.out.println(Thread.currentThread().getName());
                }
            }
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName()+ " is interrupted");
            e.printStackTrace();
        }
    }

    public static void main(String [] args) {

        final NotInterruptThread itt = new NotInterruptThread();
        final Object lock1 = new Object();
        final Object lock2 = new Object();
        Thread t1 = new Thread(new Runnable(){
            public void run() {
                itt.deathLock(lock1, lock2);
            }
        },"A");
        Thread t2 = new Thread(new Runnable(){
            public void run() {
                itt.deathLock(lock2, lock1);
            }
        },"B");

        t1.start();
        t2.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 中断线程t1、t2
        t1.interrupt();
        t2.interrupt();
    }
}
⚠️ **GitHub.com Fallback** ⚠️