内容简介:Java中断机制为我们提供了一种"试图"停止一个线程的方法。设想我们有一个线程阻塞在一个耗时的I/O中,我们又不想一直等下去,那么我们怎么样才能停止这个线程呢?答案就是Java的中断机制。Java线程的状态包括:NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING和TERMINATED总共六种状态:一个线程新建后,在调用该线程的start()方法之前,该线程的状态就是NEW;当线程的start()方法被调用之后,该线程已经准备被CPU调度,此时线程的状态就是RUNNABLE;
Java中断机制为我们提供了一种"试图"停止一个线程的方法。设想我们有一个线程阻塞在一个耗时的I/O中,我们又不想一直等下去,那么我们怎么样才能停止这个线程呢?答案就是 Java 的中断机制。
从Java线程的状态说起
Java线程的状态包括:NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING和TERMINATED总共六种状态:
public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; } 复制代码
一个线程新建后,在调用该线程的start()方法之前,该线程的状态就是NEW;当线程的start()方法被调用之后,该线程已经准备被CPU调度,此时线程的状态就是RUNNABLE;如果一个对象的monitor lock被其他线程获取了,这个时候我们再通过synchronized关键字去获取改对象的monitor lock时,线程就会进入BLOCKED状态;通过调用Thread#join()方法或者Object#wait()方法(不设置超时时间,with no timeout)或者LockSupport#park()方法可以让一个线程从RUNNABLE状态转为WAITING状态;TIMED_WAITING指线程处于等待中,但是这个等待是有期限的(),通过调用Thread#sleep(),Object#wait(long timeout),Thread#join(long timeout),LockSupport#parkNanos(),LockSupport#partUnitl()都可使线程切换到TIMED_WAITING状态。最后一种状态TERMINATED是指线程正常退出或者异常退出后的状态。下面这张图展示了这六种线程状态之间的相互转换关系:
当线程处于等待状态或者有超时的等待状态时(TIMED_WAITING,WAITING)我们可以通过调用线程的interrupt()方法来中断线程的等待,此时线程会抛InterruptedException异常。例如Thread.sleep()方法: public static native void sleep(long millis) throws InterruptedException;
此方法就会抛出这类异常。 但是当线程处于BLOCKED状态或者RUNNABLE(RUNNING)状态时,调用线程的interrupt()方法也只能将线程的状态位设置为true。停止线程的逻辑需要我们自己去实现。
中断原理
查看java源码可以看到Thread类中提供了跟中断相关的一些方法:
public void interrupt() { if (this != Thread.currentThread()) checkAccess(); 1 synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { 2 interrupt0(); // Just to set the interrupt flag 3 b.interrupt(this); 4 return; } } interrupt0(); } 复制代码
interrupt()方法用于中断一个线程,首先在第1处中断方法会判断当前caller线程是否具有中断本线程的权限,如果没有权限则抛出SecurityException异常。然后在2处方法会判断阻塞当前线程的对象是否为空,如果不为空,则在3处先设置线程的中断flag为true,然后再由Interruptible对象(例如可以中断的I/O操作)去中断操作。从中我们也可以看到如果当前线程不处于WAITING或者TIMED_WAITING状态,则interrupt()方法也只是仅仅设置了该线程的中断flag为true而已。
public static boolean interrupted() { return currentThread().isInterrupted(true); } 复制代码
再看interrupted()方法,该方法是一个静态的方法,最终会调用具体线程实例的isInterrupted()方法:
public boolean isInterrupted() { return isInterrupted(false); } private native boolean isInterrupted(boolean ClearInterrupted); 复制代码
interrupted()方法第一次调用时会返回当前线程的中断flag,然后就清除了这个状态位,但是isInterrupted()方法不会清除该状态位。因此一个线程中断后,第一次调用interrupted()方法会返回true,第二次调用就返回false,因此第一次调用后该状态位已经被清除了。但是同样一个线程中断后,多次调用isInterrupted()方法都会返回true。这是两者的不同之处,但是两者最终都会调用一个名为isInterrupted的native方法。
中断的处理
java中一般方法申明了throws InterruptedException的就是可以中断的方法,比如:ReentrantLock#lockInterruptibly,BlockingQueue#put,Thread#sleep,Object#wait等。当这些方法被中断后会抛出InterruptedException异常,我们可以捕获这个异常,并实现自己的处理逻辑,也可以不处理,继续向上层抛出异常。但是记住不要捕获异常后什么都不做并且不向上层抛异常,也就是说我们不能" 吞掉 "异常。 对于一些没有抛出InterruptedException的方法的中断逻辑只能由我们自己去实现了。例如在一个大的循环中,我们可以自己判断当前线程的中断状态然后选择是否中断当前操作:
public class InterruptTest { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new Runnable() { @Override public void run() { // 循环中检测当前线程的中断状态 for (int i = 0; i < Integer.MAX_VALUE && !Thread.currentThread().isInterrupted(); i++) { System.out.println(i); } } }); thread.start(); Thread.sleep(100); thread.interrupt(); } } 复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Linux中断一网打尽(2) - IDT及中断处理的实现
- Kernel trace tools(一):中断和软中断关闭时间过长问题追踪
- 展开中断或忽略的序号
- haskell – 让重命名功能中断代码
- Cloudflare 接口服务中断故障复盘与思考
- [译] 使用 PDB 避免 Kubernetes 集群中断
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
程序是怎样跑起来的
[日] 矢泽久雄 / 李逢俊 / 人民邮电出版社 / 2015-4 / 39.00元
本书从计算机的内部结构开始讲起,以图配文的形式详细讲解了二进制、内存、数据压缩、源文件和可执行文件、操作系统和应用程序的关系、汇编语言、硬件控制方法等内容,目的是让读者了解从用户双击程序图标到程序开始运行之间到底发生了什么。同时专设了“如果是你,你会怎样介绍?”专栏,以小学生、老奶奶为对象讲解程序的运行原理,颇为有趣。本书图文并茂,通俗易懂,非常适合计算机爱好者及相关从业人员阅读。一起来看看 《程序是怎样跑起来的》 这本书的介绍吧!