Java并发--InterruptedException机制

原创声明 :本文系作者原创,谢绝个人、媒体、公众号或网站 未经授权 转载,违者追究其法律责任。

近期重新学习juc中的 Reentrant Locklock类源码,虽然之前有相当长时间从事IM编发编程,但每次回顾IM设计,都会有新的收获。这次从中重新收获java中断类InterruptedException的理解。

java中断响应是描述当一个线程或方法A处于运行、阻塞或死锁状态时,外界(通常指其他线程、系统IO等)对A的影响能否让A线程或者方法抛出InterruptedException异常并提前返回,如果会提前返回并且抛出InterruptedException,就叫可中断响应方法或线程,如果不会抛出InterruptedException,就叫不可中断线程或方法。

一、中断协议
优雅地响应中断甚至怎样抛出InterruptedException类比较高深,与C、C++语言不同,Java没有提供一种安全直接的方法来直接停止某个线程,而是提供了一个种叫做中断协议的东西,开放给程序员自己去实现线程怎么终止,Java语言的这种中断机制是一种协议机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程或方法自己处理中断。这好比是家里的父母叮嘱在外的子女要注意身体,但子女是否注意身体,怎么注意身体则完全取决于自己。

每个线程都有一个与线程是否已中断的相关联的 Boolean 属性,用于表示线程的中断状态(interrupted status)。中断状态初始时为 false;当一个线程A通过调用 threadB.interrupt() 中断线程B时,会出现以下两种情况之一。如果那个线程B在执行一个低级可中断阻塞方法,例如 Thread.sleep()、 Thread.join() 或 Object.wait(),那么它将取消阻塞并抛出 InterruptedException。否则, interrupt() 只是设置线程B的中断状态。 在被中断线程B中运行的代码以后可以轮询中断状态,看看它是否被请求停止正在做的事情。中断状态可以通过 Thread.isInterrupted() 来读取,并且可以通过一个名为 Thread.interrupted() 的操作读取和清除。
Thread类提供如下3个操作维护这个Boolean变量:
方法
描述
public static boolean interrupted
测试当前线程是否已经中断。调用该方法后,线程的中断状态会被重置。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。此方法内部调用的是私有的 Thread.currentThread().isInterrupted(true);  带true参数的isInterrupted方法意思是返回当前线程的中断状态,然后reset当前线程的中断状态
public boolean isInterrupted()
测试线程是否已经中断。线程的中断状态不受该方法的影响。内部调用的私有的参数为false的isInterrupted(false)方法
public void interrupt()
中断线程,将中断状态设为true

二、发送中断信号
在java中,从开发者角度,中断一个线程的唯一方式是调用interrupt方法。中断是通过调用Thread.interrupt()方法来做的. 这个方法通过修改了被调用线程的中断状态来告知那个线程, 说它被中断了. 对于非阻塞中的线程, 只是改变了中断状态, 即Thread.isInterrupted()将返回true。
java类库中的有些类的方法也可能会调用中断,如FutureTask中的cancel方法,如果传入的参数为true,它将会在正在运行异步任务的线程上调用interrupt方法,又如ThreadPoolExecutor中的shutdownNow方法会遍历线程池中的工作线程并调用线程的interrupt方法。只不过也只是向线程发出中断信号而已,至于接收线程怎样对中断响应,则要由接收线程自己决定。

三、是否接收到中断
对于非阻塞中的线程,如果 想要知道线程是否已收到中断信号,那么只能通过 Thread.currentThread().isInterrupted()方法  手动检测,通常放在一个while循环当中。如果线程已被阻塞,线程是没有占用CPU的,也就没法通过 Thread.currentThread().isInterrupted() 去检测,这里需要使用java底层的机制来检测,这个底层的机制在开发层面,即体现在InterruptedException类 。非阻塞中的线程,可以如下代码检测线程中断信号:

        public class InterrupteThread extends Thread{

            public void run(){  

                while(true){  

                    if(Thread.currentThread().isInterrupted()){  

                        System.out.println("Someone interrupted me.");  

                    }  

                    else{  

                        System.out.println("Thread is Going...");              }

                }  

            }      public static void main(String[] args) throws InterruptedException {  

                InterrupteThread  t = new InterrupteThread ();  

                t.start();  

                Thread.sleep(3000);  

                t.interrupt();  

            }  

        }  


四、响应中断
在Core Java中有这样一句话:” 没有任何java语言方面的需求要求一个被中断的程序应该终止。中断一个线程只是为了引起该线程的注意,被中断线程可以决定如何应对中断  “。这里分 可响应中断线程和不可响应中断线程两种场景。
可响应中断线程通常指线程处于阻塞状态时,如果收到中断信号时,是否会取消阻塞状态,所以也叫 可取消的阻塞状态中的线程。如上面所述,可取消阻塞状态更像一种机制,java中Thread.sleep(), Object.wait(), Thread.join()等都实现了这种机制,所有当你使用sleep这些方式时,java会从语法级别强制你使用try...cacth...语句接收InterruptedException,即阻塞中的线程如果收到中断信号,会通抛出InterruptedException异常将具体响应处理转移给开发者,同时把中断状态置为true,我们可以看sleep等方法的源码,这些方法中都会调用Thread.interrupted()对中断状态进行复位。而像 Thread.suspend, Thread.stop这些方式,也是因为由于没有使用可中断机制而被jdk早早 Deprecated
不可响应中断线程是指阻塞中的线程或者在运行线程不会对中断信号作任何响应,如输入和输出流类会阻塞等待 I/O 完成,但是它们不抛出 InterruptedException,而且在被中断的情况下也不会退出阻塞状态. 然而,对于Socket I/O,如果一个线程关闭套接字,则那个套接字上的阻塞 I/O 操作将提前结束,并抛出一个 SocketException。java.nio 中的非阻塞 I/O 类也不支持可中断 I/O,但是同样可以通过关闭通道或者请求 Selector 上的唤醒来取消阻塞操作。通过synchronized尝试获取一个内部锁的操作(进入一个 synchronized 块)是不能被中断的,但是 本篇提到的juc ReentrantLock则支持可中断的获取模式即 tryLock(long time, TimeUnit unit)。

总结
本文重点讲述了InterruptedException类后面所包含的机制及来龙去脉,在你彻底明白InterruptedException类后面的机制后,怎样使用InterruptedException类,则变成很体现技术功底的学问,一种具有代表性的错误错误处理InterruptedException是“生吞”-- 捕捉它,然后什么也不做,然而这种方法忽略了这样一个事实:这期间可能发生中断,而中断可能导致应用程序丧失及时取消活动或关闭的能力。
try {
  Thread.sleep( 100 );
} catch (InterruptedException ex) {
   // 什么也不做
}

更好的习惯是存在 InterruptedException 抛出的地方,都要继续向上抛出 InterruptedException异常,以便上层应用能检测到中断。 不要丢失 InterruptedException,这一点非常重要。我们不能吞噬该异常并继
续运行。这严重违背了 Java 多线程原则。所有者(线程的所有者)要求停止线程,而我们却将其忽略,这是非常不好的想法。
try {
  Thread.sleep( 100 );
} catch (InterruptedException ex) {
   throw new InterruptedException (ex);
}

参考:

猜你喜欢

转载自blog.csdn.net/meiliangdeng1990/article/details/80559012
今日推荐