利用标志变量
通过检查volatile类型的标志变量控制线程中断,不使用volatile类型的标志变量会导致jvm对内存的优化引起死循环
public class StopThread {
private static volatile boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
public void run() {
int i = 0;
while (!stopRequested) {
i++;
}
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
interrupt中断
如果不通过像上述这样的标志变量中断线程,也可以利用Thread类的相关方法进行中断
header 1 | header 2 |
---|---|
public static boolean interrupted | 测试当前线程是否已经中断。线程的中断状态 由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。 |
public boolean isInterrupted() | 测试线程是否已经中断。线程的中断状态不受该方法的影响。 |
public void interrupt() | 中断线程。 |
Thread.interrupt方法可以中断线程正在执行的会抛出InterruptedException的方法时,执行interrupt方法的时候该线程会抛出异常,以异常的方式中断线程,比如下面这个例子
例一
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(60);
} catch (InterruptedException e) {
//执行后续清理
System.err.println("this thread has been notified...");
}
}
});
thread.start();
//等待让thread线程开始运行
TimeUnit.SECONDS.sleep(1);
thread.interrupt();
System.out.println("finish");
}
sleep方法会抛出InterruptedException,sleep方法是一个可中断的方法,上面这个例子thread线程处于sleep状态时,mian线程调用interrupt方法中断了正在睡眠的线程.在catch中务必进行资源的清理工作,thread.interrupt()方法不能中断正在等待锁或者正在等待I/O的线程.如果想要关闭I/O等待的资源可以直接关闭底层资源来终止线程,而对于锁等待的线程终止在下面会有案例.在这个例子中尤其要注意一点,InterruptedException异常被捕获之后,中断状态会被重设为false
例二
Java中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理中断。在下面这个例子中,我们检查Thread类的标志变量的方法来中断线程
class Compute implements Runnable {
@Override
public void run() {
double d = 1.0;
while (!Thread.interrupted()) {
d = d + (Math.PI + Math.E) / d;
}
}
}
public class NonInterrupTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Compute());
thread.start();
TimeUnit.SECONDS.sleep(2);
thread.interrupt();
System.out.println("interrupt...");
}
}
再强调下上文提到的一点Thread.interrupted()方法在调用之后Thread的标志变量会被复位
ReentrantLock
上文提到对于正在进行锁等待的线程,我们无法通过Thread.interrupt方法立即中断线程,那么有没有一种锁可以在被interrupt时中断锁等待呢?实际上我们可以通过lockInterruptibly方法实现
class BlockMutex {
private Lock lock = new ReentrantLock();
public BlockMutex() {
lock.lock();
}
public void f() {
try {
lock.lockInterruptibly();
System.out.println("lock acquire in f() ");
} catch (InterruptedException e) {
//资源清理
e.printStackTrace();
}
}
}
class MyBlockThread implements Runnable {
private BlockMutex blockMutex;
public MyBlockThread(BlockMutex blockMutex) {
this.blockMutex = blockMutex;
}
@Override
public void run() {
System.out.println("waiting lock in f()");
blockMutex.f();
System.out.println("broken out of blocked call");
}
}
public class MyLockInterruptTest {
public static void main(String[] args) throws InterruptedException {
//线程创建BlockThread同时就持有了blockMutex锁
BlockMutex blockMutex = new BlockMutex();
Thread thread = new Thread(new MyBlockThread(blockMutex));
thread.start();
TimeUnit.SECONDS.sleep(1);
//中断锁等待
thread.interrupt();
}
}
主线程在创建BlockMutex时就持有对象锁,另一线程在等待获取BlockMutex对象锁时是就中断该线程