南宁Java培训
达内南宁JAVA中心

13471155827
qq:269520999

热门课程

Java 线程的中断机制

  • 时间:2018-02-08 16:17
  • 发布:TEDU
  • 来源:网络

今天我们聊聊 Java 线程的中断机制。

线程中断机制提供了一种方法,有两种常见用途:

  1. 将线程从阻塞等待中唤醒,并作出相应的“受控中断”处理。
  2. 尝试告知目标线程:请打断现有处理流程,响应新的命令。
以第一种用途为例,请看以下代码:

synchronized(lock) {try{while(!check()) { lock.wait(1000); } }catch(InterruptedException e) { e.printStackTrace(); } }

这段代码使用了 Java 提供的 wait/notify 机制,线程执行 lock.wait() 会阻塞,有三种情况使线程恢复运行。

  1. 超时 1000ms 结束,正常执行下一句代码。

  2. 另一个线程执行下述代码主动唤醒

synchronized(lock) { lock.notifyAll();// or lock.notify();}

  1. 这也会正常执行下一句代码。

  2. 另一个线程要求等待的线程“中断”

// 拿到等待中的线程的引用Thread a; a.interrupt();

被“中断”的线程 a,会在 lock.wait() 处抛出 InterruptedException 异常。

综上所述,你可以认为 object.wait() 内部在做这些事:

booleancheckTimeout = timeout >0; Thread current = Thread.currentThread(); lock.addWaiter(current);while(!current.isNotified()) {if(current.isInterrupted()) { current.clearInterrupted();thrownewInterruptedException(); }if(checkTimeout) {if(timeout ==0)break; timeout--; } }

这不完全准确,因为 wait 不使用这种“忙轮询”的方式做检查,但关于标志位的判断逻辑是正确的。

让我们从上文所述的“手动发出中断”这一操作开始探究,

// sun.nio.ch.InterruptiblepublicinterfaceInterruptible{voidinterrupt(Thread var1); }// java.lang.ThreadprivatevolatileInterruptible blocker;privatefinalObject blockerLock =newObject();publicvoidinterrupt(){if(this!= Thread.currentThread()) checkAccess();synchronized(blockerLock) { Interruptible b = blocker;if(b !=null) { interrupt0(); b.interrupt(this);return; } } interrupt0(); }// Just to set the interrupt flagprivatenativevoidinterrupt0();

能够看出,thread.interrupt() 先判断权限,然后实际调用 interrupt0() 设置线程的中断标志,如果当前线程有 nio 的 Interruptible 那么还会回调它。

注意,interrupt0() 只是设置了线程的中断标志。

当一个线程并不阻塞,没有在 object.wait(), thread.join(), Thread.sleep() 等不受 Java 程序逻辑控制的区域时,那么会发生什么事情?答案是不会发生任何事情,线程是否被打断只能通过主动地检查中断标志得知。

怎么检查?Thread 暴露了两个接口,Thread.interrupted() 和 thread.isInterrupted()。

// java.lang.Threadpublicstaticbooleaninterrupted(){returncurrentThread().isInterrupted(true); }publicbooleanisInterrupted(){returnisInterrupted(false); }privatenativebooleanisInterrupted(booleanclearInterrupted);

能够看出,两者都是依靠内部的 isInterrupted(boolean),而它会返回线程是否被打断,并根据需要清空中断标志。

当一个函数调用会发生阻塞,Java 库函数在阻塞的源头签名里标记 throws InterruptedException,并要求编写 try catch 处理中断。

当线程发生了阻塞,就像上文所述,Java 检查到中断标志,先将其清除,然后抛出 InterruptedException。

// java.lang.Objectpublicfinalvoidwait()throwsInterruptedException{ wait(0); }publicfinalnativevoidwait(longtimeout)throwsInterruptedException;

如果一个线程收到 InterruptedException,之后仍然执行了会引发阻塞的代码,它将像“没事人”一样继续阻塞住。因为 Java 在内部将中断标志清除了!

我们常见地编写以下三类处理 InterruptedException 的代码:

将 InterruptedException 交由上层处理。

publicvoidfoo()throwsInterruptedException{synchronized(lock) { lock.wait(); } }

遇到 InterruptedException 重设中断标志位。

try{synchronized(lock) { lock.wait(); } }catch(InterruptedException e) { Thread.currentThread().interrupt();//break;}

先忙完,再重新抛出 InterruptedException。

publicvoidbar()throwsInterruptedException{ InterruptedException ie =null;booleandone =false;while(!done) {synchronized(lock) {try{ lock.wait(); }catch(InterruptedException e) { ie = e;continue; } } done =true; }if(ie !=null) {throwie; } }

如果一个线程无视中断标志和 InterruptedException,它仍然能够跑的很好。但这与我们设计多线程的初衷是违背的,我们希望线程之间是和谐的有序协作以实现特定功能,因此受控线程应当对中断作出响应。而 Java 留给开发者这一自由,我们应当予以善用。

上一篇:JVM你必须知道的那​些事
下一篇:2018 年 Java,Web 和移动程序员学习的 6个框架

java练习题【含答案】

java面试题(1)

JavaWeb项目为什么我们要放弃jsp?为什么要前后端解耦?为什么要前后端分离?

Java内存管理

选择城市和中心
贵州省

广西省

海南省