【Java 并发】关于中断的几个疑问
一,Java终止线程的方法有哪些?
1,run()方法执行完成,任务完成,线程自动终止。
2,使用简单粗暴的方法,使用Thread类中的stop()方法强行终止线程。不过都说该方法过时,它会导致该线程所持有的锁被强制释放,从而被其他线程所持有,因此有可能导致与预期结果不一致。具体参考http://blog.csdn.net/dlite/article/details/4212915,有详细介绍。
3,使用中断。安全地终止线程。
二,Java中断原理是什么?
在Java中没有一种安全的抢占式方法来终止线程,因此也没安全的方法来终止任务。只有一种协作式机制,主要是中断机制。也就是说不是直接就去终止一个线程,而是告诉线程要被终止了,而需要被中断的线程自己处理中断。
每个线程都会有一个boolean类型的中断状态。当中断线程时,中断状态被设置为true。
java.lang.Thread类提供了几个方法来操作这个中断状态,这些方法包括:
public static boolean interrupted():静态的interrupted()将清除当前线程的中断状态,并返回它之前的值,这也是清除中断状态的唯一方法。
public boolean isInterrupted():返回目标线程的中断状态。
public void interrupt():中断线程。不过注意interrupt()只是改变中断状态而已,interrupt()不会直接终止一个正在运行的线程。
import java.util.concurrent.TimeUnit;
public class TestInterrupt implements Runnable {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Thread test=new Thread(new TestInterrupt());
test.start();
TimeUnit.MILLISECONDS.sleep(500);
System.out.println("调用interrupt()");
test.interrupt();
System.out.println("中断状态:"+test.isInterrupted());
}
@Override
public void run() {
// TODO Auto-generated method stub
long time = System.currentTimeMillis();
System.out.println("线程正在运行");
while ((System.currentTimeMillis() - time < 1000)) {}
System.out.println("清除线程的中断状态:" + Thread.interrupted());
System.out.println("线程是否中断:" +Thread.currentThread().isInterrupted());
while ((System.currentTimeMillis() - time < 5000)) {}
System.out.println("线程运行完成");
}
}
线程正在运行
调用interrupt()
中断状态:true
清除线程的中断状态:true
线程是否中断:false
线程运行完成
程序很简单,开始线程,然后调用interrupt()中断线程,再用interrupted()清除中断。两点需要注意:
1,interrupt()只是改变中断状态而已,interrupt()不会直接终止一个正在运行的线程。而只是传递了请求中断的消息。
2,对于静态的interrupted(),如果它返回true,说明要清除当前线程的中断状态,所以除非想屏蔽中断,否则必须处理它。如果不做任何处理,栈上层无法对中断进行相应的处理。
三,当检查到中断请求时,线程立即终止?任务立即放弃操作?处理中断的时机?
作为一种协作机制,不会强求被中断线程一定要立即进行处理。实际上,被中断线程只需在合适的时候处理即可,如果没有合适的时间点,甚至可以不处理,这时候在任务处理层面,就跟没有调用中断方法一样。“合适的时候”与任务操作的业务逻辑有关,比如不会在原子操作时中断,或者进人临界区更新状态中断。这项技术保证在更新过程发生中断时,数据结构不会被破坏。
四,响应中断的方式
1,如果遇到的是可中断的阻塞方法抛出InterruptedException,可以继续向方法调用栈的上层抛出该异常,如果是检测到中断,则可清除中断状态并抛出InterruptedException,使当前方法也成为一个可中断的方法。常见的可中断的阻塞方法:Object.wait, Thread.join和Thread.sleep
2,若有时候不太方便在方法上抛出InterruptedException,比如要实现的某个接口中的方法签名上没有throws InterruptedException,这时就可以捕获可中断方法的InterruptedException并通过Thread.currentThread.interrupt()来重新设置中断状态。
import java.util.concurrent.TimeUnit;
public class TestInterrupt implements Runnable {
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread test=new Thread(new TestInterrupt());
test.start();
System.out.println("调用interrupt()");
test.interrupt();
System.out.println("中断状态:"+test.isInterrupted());
}
@Override
//Runnable 接口中的 run()方法签名上没有throws InterruptedException
public void run() {
// TODO Auto-generated method stub
System.out.println("线程正在运行");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}
五,不好的习惯—-捕捉中断却不做任何处理
这个习惯可能大家都有过
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
在catch块中捕获中断但是没做任何处理,捕捉它,然后记录下它,但是这种方法仍然无异于生吞中断,因为调用栈中更高层的代码还是无法获得关于该异常的信息。
一种标准的做法就是通过再次调用interrupt()来恢复中断状态,以便调用栈中更高层的代码能知道中断,并对中断作出响应。而不仅仅打印信息。
如果自己知道线程中断的具体处理方法或者栈中已经没有上层代码需要知道中断信息,就可以屏蔽中断请求,
六,处理不可中断的阻塞
对于某些线程阻塞操作,JVM并不会自动抛出InterruptedException异常。例如,某些I/O操作和内部锁操作。对于这类操作,中断请求只能设置线程的中断状态,除此之外没其他作用。
1,Java.io中的同步socket I/O
读写socket的时候,InputStream和OutputStream的read和write方法会阻塞等待,但不会响应中断。不过,调用Socket的close方法后,被阻塞线程会抛出SocketException异常。
2,Java.io中的同步 I/O
当中断一个正在InterruptibleChannel上等待的线程时,将抛出CloseByInterruptException并关闭链路。当关闭一个InterruptibleChannel时,将导致所有在链路操作上阻塞的线程都抛出AsynchronousCloseException。
3,利用Selector实现的异步I/O
如果线程被阻塞于Selector.select(在java.nio.channels中),调用close()和wakeup()方法会引起ClosedSelectorException异常。
4,锁获取
如果线程在等待获取一个内部锁,我们将无法中断它。但是,利用Lock类的lockInterruptibly方法,我们可以在等待锁的同时,提供中断能力。
参考
《Java编程思想》-并发
《Java并发编程实践》
https://www.ibm.com/developerworks/cn/java/j-jtp05236.html
http://ifeve.com/java-interrupt-mechanism/