【并发编程】--- interrupt、interrupted和isInterrupted使用详解

源码地址:https://github.com/nieandsun/concurrent-study.git

1 为何不建议用stop方法中断线程

在java的世界里,Thread类是对线程概念的抽象。想要中断一个线程有两种方式:

  • (1)调用Thread类的stop方法,
  • (2)组合调用Thread类的 interrupt、interrupted和isInterrupted方法

但是第一种方式是不推荐使用的,并且JDK源码中也已经将其置为了@Deprecated。其原因为使用stop方法进行中断线程本质上是不安全的,它会直接释放掉本线程所持有的所有资源,举个简单的栗子来说,假如我们正在使用某个线程下载电影,如果该线程通过stop进行中断,则原来下载的内容将全部丢失。

正是基于以上原因,JDK提供了、并推荐使用interrupt、interrupted和isInterrupted方法用来进行线程的中断


2 interrupt、interrupted和isInterrupted方法介绍

  • interrupt() 方法 —> 发起线程中断请求,但只是请求,并不会真的把线程给中断,实际上是把线程的中断标识设置为了true;
  • isInterrupted()方法 —> 判断线程的中断标识是否为true
  • interrupted() 方法 —> 判断线程的中断标识是否为true, 方法调用完之后,会将中断标识改为false

总结如下:
在这里插入图片描述


3 interrupt、interrupted和isInterrupted方法测试

测试代码如下:

package com.nrsc.ch1.safeend;

/***
 * 如何安全中断线程 --- 继承Thread类的情况
 */
public class EndThread {

    private static class UseThread extends Thread {

        public UseThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.err.println(threadName + " interrrupt flag before=" + isInterrupted());
            //while(true){
            //while (!isInterrupted()) {
            while (!Thread.interrupted()) {

                System.out.println(threadName + " is running");
                System.out.println(threadName + "inner interrrupt flag ="
                        + isInterrupted());
            }
            System.err.println(threadName + " interrrupt flag after =" + isInterrupted());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread endThread = new UseThread("endThread");
        endThread.start();
        Thread.sleep(1);
        endThread.interrupt();//中断线程,其实设置线程的标识位true
    }
}

3.1 被中断线程感知到中断请求,但不理会

即上诉代码的条件为while(true)时

测试结果如下,可以看到endThread 线程已经感知到了中断请求,但是该线程并未终止运行

在这里插入图片描述


3.2 isInterrupted方法感知中断请求 — 仅仅判断当前线程的中断标识

即上诉代码的条件为while (!isInterrupted())时

测试结果如下,可以看到endThread 线程感知到中断线程请求后,由于!isInterrupted() 变为false,将会退出循环,并且退出循环后,当前线程的中断标识仍为true —> 其实这从3.1页可以看出来。

多提一嘴: endThread interrrupt flag after =true这句话之所以会先打印出来,相信大家应该都知道,这是由于idea打印内容到控制台是多线程的,谁先被打印出来,并不完全一定。。。

在这里插入图片描述


3.3 interrupted方法感知中断请求 — 该方法被调用后,会将中断标识改回为false

即上诉代码的条件为while (!Thread.interrupted())时

会有两种结果

  • 结果1, isInterrupted方法先感知到中断请求,如下:

在这里插入图片描述


  • 结果2, isInterrupted方法没感知到中断请求,但interrupted方法率先感知到了:

在这里插入图片描述


但是无论哪种结果,都可以证明:

  • (1)可以使用interrupted方法作为感知线程中断请求的逻辑处理
  • (2)interrupted方法被调用后会将对于该线程的中断请求重新由true改为false

4 实现Runnable接口的方式建立的线程如何安全中断

本文2,3两部分对于线程的管理,采用的是继承Thread类的方式,由于继承自Thread,所以 interrupt、interrupted和isInterrupted可以直接作为内部方法进行调用。

相信大家肯定知道,开启线程还有另外一种方式就是实现Runnable接口,那这种情况下怎样安全的中断线程呢?其实很简单: 只需要Thread.currentThread()获取到当前线程就可以了 。这里提供一个简单的栗子:

package com.nrsc.ch1.safeend;

/**
 * 类说明:实现接口Runnable的线程如何中断
 */
public class EndRunnable {

    private static class UseRunnable implements Runnable {

        @Override
        public void run() {

            System.err.println(Thread.currentThread().getName()
                    + " interrupt flag before ==" + Thread.currentThread().isInterrupted());

            while (!Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getName()
                        + " I am implements Runnable.==" + Thread.currentThread().isInterrupted());
            }
            System.err.println(Thread.currentThread().getName()
                    + " interrupt flag after ==" + Thread.currentThread().isInterrupted());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        UseRunnable useRunnable = new UseRunnable();
        Thread endThread = new Thread(useRunnable, "endThread");
        endThread.start();
        Thread.sleep(20);
        endThread.interrupt();
    }

}

5 阻塞方法中抛出InterruptedException异常后会将中断标识改回为false ★★★

比如下面的程序:

package com.nrsc.ch1.safeend;

/**
 *类说明:阻塞方法中抛出InterruptedException异常后,如果需要继续中断,需要手动再中断一次
 */
public class HasInterrputException {
	
	private static class UseThread extends Thread{
		
		public UseThread(String name) {
			super(name);
		}
		
		@Override
		public void run() {
			while(!isInterrupted()) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					System.out.println(Thread.currentThread().getName()
							+" in InterruptedException interrupt flag is "
							+isInterrupted());

					//感知到线程中断后,可以先进行资源释放,然后再手动调用一下interrupt()方法进行真正的线程中断
					//interrupt();
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()
						+ " I am extends Thread.");
			}
			System.out.println(Thread.currentThread().getName()
					+" interrupt flag is "+isInterrupted());
		}
	}

	public static void main(String[] args) throws InterruptedException {
		Thread endThread = new UseThread("HasInterrputEx");
		endThread.start();
		Thread.sleep(500);
		endThread.interrupt();
	}
}

运行结果如下:

在这里插入图片描述

为什会这样设计呢??? 这里我觉得可以和stop方法被标为@Deprecated的原因一致,就是jdk的设计者,希望你感知到线程中断请求后,可以做一些其他的事,然后再由你自己来决定是不是要中断当前线程。


也就是说如果想让上面的程序停下来,需要打开我代码中的那句被注释的代码,打开后的运行效果如下:

在这里插入图片描述


6 一点注意事项

注意: 工作中不要通过使用自定义变量的方式去中断线程。

发布了207 篇原创文章 · 获赞 244 · 访问量 46万+

猜你喜欢

转载自blog.csdn.net/nrsc272420199/article/details/104727192