Tips
-
synchronized块是不能被中断的:在synchronized关键字阻塞等待获取锁时不可中断,但进入同步块后可被中断,表现同下文对两种线程状态的中断。参考: 为什么Synchronized不可中断?-腾讯云开发者社区-腾讯云
-
ReentrantLock支持可中断的获取模式,即 ReentrantLock.lockInterruptibly()。
-
不是所有的阻塞方法收到中断后都可以取消阻塞状态,输入和输出流类会阻塞等待 I/O 完成,但是它们不抛出 InterruptedException,而且在被中断的情况下也不会退出阻塞状态。
1,中断两种状态的线程
强行终止是很危险的事情,优雅的方法就是,给那个线程一个中断信号, 让它自己决定该怎么办,这就是中断。
中断是通过调用Thread.interrupt()方法来执行的:
-
中断非阻塞中的线程:只是改变了中断标记,即Thread.isInterrupted()将返回true。
-
中断可取消的阻塞状态中的线程:比如sleep、wait、join,这个线程收到中断信号后,会抛出InterruptedException,同时会把中断状态置回为false。 如果先中断然后才sleep,也是会执行到sleep的时候立马抛异常(中断标志为true了)。
注意:如果线程是阻塞在LockSupport
.
park
()上,给线程中断后,不会抛出异常,线程唤醒继续执行,且线程中断标志为true。
2,终止“阻塞状态”的线程
通常,我们通过“中断”方式终止处于“阻塞状态”的线程。
当线程由于被调用了sleep()、wait()、join()等方法而进入阻塞状态,若此时调用线程的interrupt()将线程的中断标记设为true。
由于处于阻塞状态,中断标记会被重置到fasle
,同时产生一个InterruptedException异常。将InterruptedException放在适当的为止就能终止线程,形式如下:
@Override
public void run() {
try {
while (true) {
// 执行任务…
Thread.sleep(1000);
}
} catch (InterruptedException ie) {
// 由于产生InterruptedException异常,退出while(true)循环,线程终止!
}
}
说明:在while(true)中不断的执行任务,当线程处于阻塞状态时,调用线程的interrupt()产生InterruptedException中断。中断的捕获在while(true)之外,这样就退出了while(true)循环!
注意:对InterruptedException的捕获务一般放在while(true)循环体的外面,否则就需要额外处理退出循环。形式如下:
@Override
public void run() {
while (true) {
try {
// 执行任务...
} catch (InterruptedException ie) {
// InterruptedException在while(true)循环体内。
// 当线程产生了InterruptedException异常时,while(true)仍能继续运行!需要手动退出
break;
}
}
}
3,终止“运行状态”的线程
通常,我们通过“标记”方式终止处于“运行状态”的线程。其中,包括“中断标记”和“额外添加标记”。
(01) 通过“中断标记”终止线程。形式如下:
@Override
public void run() {
while (!isInterrupted()) {
// 执行任务...
}
}
说明:isInterrupted()是判断线程的中断标记是不是为true。
注意:interrupt()并不会终止处于“运行状态”的线程!它会将线程的中断标记设为true。
(02) 通过“额外添加标记”。形式如下:
private volatile boolean flag = true;
protected void stopTask() {
flag = false;
}
@Override
public void run() {
while (flag) {
// 执行任务...
}
}
注意:将flag定义为volatile类型,是为了保证flag的可见性。
4,通用的终止线程形式
综合线程处于“阻塞状态”和“运行状态”的终止方式,比较通用的终止线程的形式如下:
@Override
public void run() {
try {
// 1. isInterrupted()保证,只要中断标记为true就终止线程。
while (!isInterrupted()) {
// 执行任务...
}
} catch (InterruptedException e) {
// 2. InterruptedException异常保证,当InterruptedException异常产生时,线程被终止。
}
}
5,实际应用
由于
处于阻塞状态的线程,被中断后抛出exception并置回中断状态,有时候是不利的,因为这个中断状态可能会作为别的线程的判断条件,所以稳妥的办法是在处理exception的地方把状态复位:
boolean interrupted = false;
try {
while (true) {
try {
return blockingQueue.take();
} catch (InterruptedException e) {
interrupted = true;
break;
}
}
} finally {
if (interrupted){
Thread.currentThread().interrupt();
}
}
当代码调用中须要抛出一个InterruptedException,你可以选择把中断状态复位,也可以选择向外抛出InterruptedException,由外层的调用者来决定。