嚼烂interrupt,interrupted和isInterrupted

前言

前面在讲java中的线程中断机制中,我们提到了用stop和suspend来强制终止线程的不安全性,当时也提到了interrupt方法。这里再和大家一起学习下Java多线程中的interrupt,interrupted和isInterrupted。

interrupt的伸冤之路

首先对于interrupt方法,他是用于中断线程的,调用该方法的线程的状态将被置为"中断"状态。注意:线程中断仅仅是设置线程的中断状态位,不会停止线程。需要用户自己去监视线程的状态为并做处理。支持线程中断的方法(也就是线程中断后会抛出InterruptedException的方法,比如说sleep,wait等方法)就是在监视线程的中断状态,一旦线程的中断状态被置为“中断状态”,就会抛出中断异常。

接下来,我们再来看看isInterrupted。isInterrupted是用来判断线程是否处于线程中断状态。如果是返回true,否则返回false。就如同下图中介绍的

下面我们来看个例子,看看interrupt如何被冤枉了

public class Interrupt {
    public static void main(String[] args) throws Exception {
	Thread t = new Thread(new Worker());
	t.start();
	Thread.sleep(200);
	t.interrupt();
	System.out.println("Main thread stopped.");
    }
	
    public static class Worker implements Runnable {
	public void run() {
	    System.out.println("Worker started.");
		try {
		    Thread.sleep(500);
		} catch (InterruptedException e) {
		    System.out.println("Worker IsInterrupted:" + 
					Thread.currentThread().isInterrupted());
		}
	    System.out.println("Worker stopped.");
	}
    }
}

上面代码是主线程main启动了一个子线程Worker,然后让worker睡500ms,而main睡200ms,之后main调用worker线程的interrupt方法去中断worker,worker被中断后打印中断的状态。下面是执行结果:

Worker started.
Main thread stopped.
Worker IsInterrupted: false
Worker stopped.

看到上面的结果。估计认真看完上面对于interrupt和isInterrupted介绍的同学,一副问号脸,interrupt不是说好的修改线程为中断状态么,为啥isInterrupted返回的还是false?interrupt不要面子的?

稍安勿躁,其实上面的问题是出在了InterruptedException上面。我们先来看一段来自InterruptedException 的独白

InterruptedException - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.

原来是InterruptedException 这小子搞得鬼。由于调用了Thread.sleep(500)方法,而sleep()方法上面也说到会抛出InterruptedException 异常,这就导致了interrupt设定的中断状态被清除了,所以isInterrupted返回false就很好解释了。

interrupted和isInterrupted的恩恩怨怨

我们来仔细看看这两位的介绍,首先是interrupted

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

再是isInterrupted

public boolean isInterrupted() {
    return isInterrupted(false);
}

这两个方法一个是static的,一个不是,但实际上都是在调用同一个方法,只是interrupted方法传入的参数为true,而inInterrupted传入的参数为false。所以要看清楚interrupted和isInterrupted的真实面目,我们还是要继续往下看

/**
 * Tests if some Thread has been interrupted.  The interrupted state
 * is reset or not based on the value of ClearInterrupted that is
 * passed.
 */
private native boolean isInterrupted(boolean ClearInterrupted);

这是一个native方法,看不到源码没有关系,参数名字ClearInterrupted已经清楚的表达了该参数的作用----是否清除中断状态。方法的注释也清晰的表达了“中断状态将会根据传入的ClearInterrupted参数值确定是否重置”。所以,静态方法interrupted将会清除中断状态(传入的参数ClearInterrupted为true),而实例方法isInterrupted则不会(传入的参数ClearInterrupted为false)。

为了验证上面的区别,我们接着看下面的例子

public class Interrupt  {
    public static void main(String[] args) throws Exception {
	Thread t = new Thread(new Worker());
	t.start();
	Thread.sleep(200);
	t.interrupt();
	System.out.println("Main thread stopped.");
    }
	
    public static class Worker implements Runnable {
	public void run() {
	System.out.println("Worker started.");	
	try {
	    Thread.sleep(500);
	} catch (InterruptedException e) {
	    Thread curr = Thread.currentThread();
	    //再次调用interrupt方法中断自己,将中断状态设置为“中断”
	    curr.interrupt();
	    System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
	    System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
	    System.out.println("Static Call: " + Thread.interrupted());//clear status
	    System.out.println("---------After Interrupt Status Cleared----------");
	    System.out.println("Static Call: " + Thread.interrupted());
	    System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
            System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
	}	
	System.out.println("Worker stopped.");
	}
    }
}

执行结果:

Worker started.
Main thread stopped.
Worker IsInterrupted: true
Worker IsInterrupted: true
Static Call: true
---------After Interrupt Status Cleared----------
Static Call: false
Worker IsInterrupted: false
Worker IsInterrupted: false
Worker stopped.

从执行结果也可以看到,前两次调用isInterrupted方法都返回true,说明isInterrupted方法不会改变线程的中断状态,而接下来调用静态的interrupted()方法,第一次返回了true,表示线程被中断,第二次则返回了false,因为第一次调用的时候已经清除了中断状态。最后两次调用isInterrupted()方法就肯定返回false了。

上面的代码中我们看到,我们在catch块中重新调用了interrupt方法,重置了中断状态,所以后面的isInterrupt方法才会返回true。那么我们在什么情况下需要在catch块中去重置中断状态呢?其实如果我们的方法中不能抛出InterruptedException(就像这里的Thread.sleep语句放在了Runnable的run方法中,这个方法不允许抛出任何受检查的异常),但又想告诉上层调用者这里发生了中断的时候,就只能在catch里面重置中断状态了。就如同下面的例子

public class TaskRunner implements Runnable {
    private BlockingQueue<Task> queue;
 
    public TaskRunner(BlockingQueue<Task> queue) { 
        this.queue = queue; 
    }
 
    public void run() { 
        try {
             while (true) {
                 Task task = queue.take(10, TimeUnit.SECONDS);
                 task.execute();
             }
         } catch (InterruptedException e) { 
             // Restore the interrupted status
             Thread.currentThread().interrupt();
         }
    }
}

总结

上面介绍了interrupt,interrupted和isInterrupt方法在线程中断的区别。只为大家更好的理解这三者的关系和区别。本文参考(借鉴)了 interrupt、interrupted和isInterrupted的区别 的内容,我用自己的语言稍微组织了下,便于自己理解学习。

原文链接:https://blog.csdn.net/hj7jay/article/details/53462553#

猜你喜欢

转载自blog.csdn.net/qq_28165595/article/details/83757274