前言
其实应该很多人都对线程中断很模糊,java其实并没有给出自带的安全立即退出线程的方法,但是它提供了一个帮助实现安全退出的机制。
java中的中断机制
java给出了中断机制,但是可能并不是像大多数人那样想,调用如interrupt线程会进行立刻的中断,实际上java的中断机制是提供一个线程控制的帮助,提供中断机制可以判断出线程是不是应改被中断,而实际的中断处理应该在我们的run中进行实现。
interrupt isInterrupted interrupted(jdk中没有被废除的中断方案)
interrupted: 是Theard静态方法,一般用于线程内部进行检查并且判断做真正的中断处理。检查线程是否已经中断,如果是中断那么进行中断清除,也就是两次调用之后正常来说会返回false,但是第一次进行清除之后还是返回true(但是如果在第一次调用之后又进行了中断也就是在第一次清除之后,第二次调用interrupted 之前又进行了中断,那么就会返回true了)。
isInterrupted:对象方法,可以被其他线程调用,用于判断线程是不是是不是已经中断,并且不会影响线程的中断状态。
interrupt:对象方法,进行中断标识设置,
class Theard1 extends Thread{
@Override
public void run() {
while (true){
System.out.println(Thread.currentThread().isInterrupted());
}
}
}
public class InterruptTest {
public static void main(String[] args) throws InterruptedException {
Theard1 theard1=new Theard1();
theard1.start();
theard1.interrupt();
System.out.println(theard1.isInterrupted());
}
}
在 theard1.interrupt();打上线程断点,在这个方法执行之前,theard1线程都是返回false,在mian方法执行之后theard1线程总是返回true。
使用interrupt isInterrupted interrupted 实现线程中断
class Theard1 extends Thread{
@Override
public void run() {
while (Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().isInterrupted());
}
}
}
public class InterruptTest {
public static void main(String[] args) throws InterruptedException {
Theard1 theard1=new Theard1();
theard1.start();
theard1.interrupt();
System.out.println(theard1.isInterrupted());
}
}
// 也就是通过Thread.currentThread().isInterrupted() 判断是不是应该进行中断处理
InterruptedException
阻塞状态的线程,进行interrupt会抛出InterruptedException异常,阻塞状态比如线程处理sleep,wait方法等。
如果遇到的是可中断的阻塞方法抛出InterruptedException,可以继续向方法调用栈的上层抛出该异常,如果是检测到中断,则可清除中断状态并抛出InterruptedException,使当前方法也成为一个可中断的方法。
若有时候不太方便在方法上抛出InterruptedException,比如要实现的某个接口中的方法签名上没有throws InterruptedException,这时就可以捕获可中断方法的InterruptedException并通过Thread.currentThread.interrupt()来重新设置中断状态。如果是检测并清除了中断状态,亦是如此。
stop(已经被废弃)
stop也是一种java的中断手段,Thread.stop方法已经不推荐使用了。而在某些方面Thread.stop与中断机制有着相似之处。如当线程在等待内置锁或IO时,stop跟interrupt一样,不会中止这些操作;当catch住stop导致的异常时,程序也可以继续执行,虽然stop本意是要停止线程,这么做会让程序行为变得更加混乱。
那么它们的区别在哪里?最重要的就是中断需要程序自己去检测然后做相应的处理,而Thread.s.lang.Error的子类。
class Theard1 extends Thread{
@Override
public void run() {
try {
while (true){
System.out.println(1);
}
} catch (Error err) {
err.printStackTrace();
}
}
}
public class InterruptTest {
public static void main(String[] args) throws InterruptedException {
Theard1 theard1=new Theard1();
theard1.setName("1");
theard1.start();
TimeUnit.SECONDS.sleep(1);
theard1.stop();
}
}
注意捕获操作需要在线程内,线程内部会响应stop中断抛出一个异常,其实一般我们不会去使用stop,如果真是使用这个方法一般我们回对捕获异常之后对一些资源比如连接之类的进行释放。
自定义方式进行中断 (通过volatile共享变量)
其实我们可以使用自己编写代码进行中断判断并且中断线程运行,只需要voliate关键词修饰就可以在多线程之间保证共享变量的即时可见。
class Theard2 extends Thread{
@Override
public void run() {
while (!InterruptTest.stop){
System.out.println(Thread.currentThread().getName());
}
}
}
public class InterruptTest {
public static volatile boolean stop=false;
public static void main(String[] args) throws InterruptedException {
Theard2 theard2=new Theard2();
theard2.setName("2");
theard2.start();
TimeUnit.SECONDS.sleep(1);
stop=true;
}
}
总结
显然,作为一种协作机制,不会强求被中断线程一定要在某个点进行处理。实际上,被中断线程只需在合适的时候处理即可,如果没有合适的时间点,甚至可以不处理,这时候在任务处理层面,就跟没有调用中断方法一样。“合适的时候”与线程正在处理的业务逻辑紧密相关,例如,每次迭代的时候,进入一个可能阻塞且无法中断的方法之前等,但多半不会出现在某个临界区更新另一个对象状态的时候,因为这可能会导致对象处于不一致状态。
处理时机决定着程序的效率与中断响应的灵敏性。频繁的检查中断状态可能会使程序执行效率下降,相反,检查的较少可能使中断请求得不到及时响应。如果发出中断请求之后,被中断的线程继续执行一段时间不会给系统带来灾难,那么就可以将中断处理放到方便检查中断,同时又能从一定程度上保证响应灵敏度的地方。当程序的性能指标比较关键时,可能需要建立一个测试模型来分析最佳的中断检测点,以平衡性能和响应灵敏性。