源码解析-Java线程中断源码理解
java线程的中断,不同于操作系统层面的中断,不是立刻将线程的行动中止,而是设置一种标识位,然后被其他调用方判断并进行处理
线程中断相关操作,在Thread类中操作 线程的中断位默认为false
interrupt(); //设置一个线程的中断位为true
static interrupted(); //调用isInterrupted(),返回当前线程中断位,并重新设置中断位为true
isInterrupted(); //返回当前线程中断位,并重新设置中断位为false
native isTnterrupted(boolean clearInterrupted); //传入是否需要重置中断位为false;并返回当前线程中断位
中断一个线程,就是设置该线程的中断位为true,具体怎么处理由客户端自己处理
中断的具体用途,例如
来自 Object 类的 wait()、wait(long)、wait(long, int),
来自 Thread 类的 join()、join(long)、join(long, int)、sleep(long)、sleep(long, int)
都会抛出throws InterruptedException, 如果线程A调用这些方法后处于阻塞状态,被线程B中断线程A的阻塞状态,会抛出InterruptedException异常 并且设置中断位false
例如Selector的select方法,轮询过程中一旦被中断方法就会返回。
如果线程阻塞在 LockSupport.park(Object obj) 方法,也叫挂起,这个时候的中断也会导致线程唤醒,但是唤醒后不会重置中断状态,所以唤醒后去检测中断状态将是 true。
总之,中断一般应用在一些长时间阻塞的场景——阻塞方法,这种方法一般都会抛出InterruptedException,往往依赖于外部条件来结束阻塞
中断的处理
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// ignore 当外部条件中断了睡眠,抛出InterruptedException异常,被我们捕获后在此处理
}
// go on
AQS对中断的两种处理方法:
两种上锁方式lock() 抢锁过程中被中断,不会处理 继续抢锁
lockInterruptibly() 如果抢锁等待过程中被中断,则抛出异常
1. lock()里的
acquire()方法,
acquireQueued() 在这里
正常执行到这里parkAndCheckInterrupt(),会使线程park()挂起,然后等待阻塞队列的上一个节点释放锁 通过unpark() 将其唤醒
如果挂起中途被外部中断,则设置中断位interrupted为true,然后循环再次抢锁,抢锁成功返回中断位,抢锁失败就继续挂起.
2. lockInterruptiby() 等待过程中如果被中断则抛异常
上来就判断,如果被中断就直接抛
重点来了, 相比于acquire()方法的等待被中断处理,在这如果等待过程中被中断了就直接抛异常!(之前那个方法是设置个中断位然后继续循环,最后这个中断位能返回获得)
在并发包中,有非常多的这种处理中断的例子,提供两个方法,分别为响应中断和不响应中断,对于不响应中断的方法,记录中断而不是丢失这个信息。如 Condition 中的两个方法就是这样的:
通常,如果方法会抛出 InterruptedException 异常,往往方法体的第一句就是