众所周知,java线程实例有个interrupt()方法,它的名字很有诱导性,但它实际上并不能立即中断线程。它只是将线程的中断状态置位为true(这是每一个线程都具有的boolean标志,通过isInterrupted()方法可以查看),而设置之后,线程根据当前的状态进行不同的后续操作。
如果线程的当前状态处于非阻塞状态,那么仅仅是线程的中断标志被修改为true不做其他操作。
如果是wait、sleep以及jion三个方法引起的阻塞,那么会将线程的中断标志重新设置为false,并抛出一个InterruptedException(异常抛出后一般就退出了,当然只是抛异常而已,如果在内部catch了该异常,线程一样可以继续执行)。这里需要注意的是哪怕线程一开始不在阻塞状态,即只要线程在进入阻塞之前调用了interrupt()来置位了,线程在进入由sleep、wait、join等引起的阻塞状态时也立即抛异常。
如果是不能被中断的IO,则只是置位而无其他操作,如各种流式IO、socket,interrupt是没有用的,但对于NIO,channel会抛ClosedByInterruptException而中断。
既然interrupt()方法并不能真正结束线程,那用什么方式呢? 最通用的一种方式就是使用共享变量(一般用volatile修饰,以保证可见性),告诉线程必须停止正在运行的任务。线程必须周期性的核查这一变量(尤其在冗余操作期间),然后有秩序地中止任务,但如果线程是阻塞状态的,那么就无法检查共享变量从而无法结束,所以interrupt()和共享变量可以一起使用。
如下代码包括了上述各种情况:
package com.thread;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class testInterrupt {
public static void main(String args[]) throws Exception {
testNotBlockingThread();
testBlockingThread();
testBlockingThread2();
testReadBlockingThread();
}
public static void testNotBlockingThread() throws InterruptedException{
NotBlockingThread notBlockingThread = new NotBlockingThread();
System.out.println("NotBlockingThread Starting...");
notBlockingThread.start();
Thread.sleep(3000);
System.out.println("Interrupting NotBlockingThread...");
notBlockingThread.interrupt();// 因为线程是运行状态的,没有阻塞,interrupt只是置位而已,并不能中断线程
Thread.sleep(3000);
System.out.println("Asking NotBlockingThread to stop...");
notBlockingThread.stop = true; // 使用共享变量时最好的结束线程的方法,但问题在于如果线程是阻塞状态的,则无法检查共享变量从而无法结束
Thread.sleep(3000);
System.out.println("Main thread stop...");
// System.exit(0); //stop application
}
public static void testBlockingThread() throws InterruptedException{
BlockingThread BlockingThread = new BlockingThread();
System.out.println("BlockingThread Starting...");
BlockingThread.start();
Thread.sleep(3000);
System.out.println("Interrupting BlockingThread...");
BlockingThread.interrupt();//因为使用了共享变量的方式,所以光interrupt是不行的,线程并不会退出
Thread.sleep(3000);
System.out.println("Asking BlockingThread to stop...");
BlockingThread.stop = true;
BlockingThread.interrupt();//两个结合使用才能让线程迅速退出
Thread.sleep(3000);
System.out.println("Main thread stop...");
// System.exit(0); //stop application
}
public static void testBlockingThread2() throws InterruptedException{
BlockingThread2 BlockingThread2 = new BlockingThread2();
BlockingThread2.start();
BlockingThread2.interrupt();//哪怕线程一开始不在阻塞状态,但只要置位了,线程进入sleep、wait、join等引起的阻塞状态也立即抛异常退出
}
public static void testReadBlockingThread() throws InterruptedException{
ReadBlockingThread readBlockingThread = new ReadBlockingThread();
readBlockingThread.start();
readBlockingThread.interrupt();//普通的流io操作不可中断,所以根本不理会interrupt,但是NIO里面的channel会抛ClosedByInterruptException而中断
}
}
class NotBlockingThread extends Thread{
public volatile boolean stop = false;
public void run(){
while (!stop) {
System.out.println("Thread is running...");
long time = System.currentTimeMillis();
while ((System.currentTimeMillis() - time < 1000)) {// 不阻塞
}
}
System.out.println("Thread exiting under request...");
}
}
class ReadBlockingThread extends Thread{
public void run(){
try {
FileInputStream inputStream=new FileInputStream(new File("I:\\debug2.log"));
FileChannel channel=inputStream.getChannel();
/*byte[] b=new byte[100];
while (inputStream.read(b)>0) {
}*/
ByteBuffer buffer=ByteBuffer.allocate(100);
while(channel.read(buffer)>0){
}
System.out.println("read done");
} catch (IOException e) {
e.printStackTrace();
}
}
}
class BlockingThread extends Thread{
public volatile boolean stop = false;
public void run(){
while (!stop) {
System.out.println("Thread is running...");
try {
Thread.sleep(1000);//阻塞
} catch (InterruptedException e) {
System.out.println("Thread interrupted...");
//Do some finishing work
}
}
System.out.println("Thread exiting under request...");
}
}
class BlockingThread2 extends Thread{
public void run(){
System.out.println("Thread is running...");
long time = System.currentTimeMillis();
while ((System.currentTimeMillis() - time < 5000)) {// 不阻塞地空转5秒
}
try {
Thread.sleep(5000);//阻塞
} catch (InterruptedException e) {
System.out.println("Thread interrupted...");
}
}
}