在实际应用中,如果想停掉某个线程,那么可以调用线程的stop()方法或者suspend()方法来强制性的停止某个线程。但是这种方法是很粗暴的,它是强制性的停止线程,这样可能会导致线程之前工作的数据丢失,所以Java已经摒弃了这些强制终止的方法。
取而代之的是一种中断机制,interrupt()方法。在JVM中每个线程都有一个boolean类型的线程是否中断的标志,调用线程中断的方法只是将这个标志置为true,这是一种优雅的线程中断的方法,它告诉线程要中断了,而不是强制中断此线程。
线程在运行时需要检测这个标志位的状态,如果不检测,即使调用了中断方法线程也不会中断。
我们可以通过调用Thread.currentThread().isInterrupted()或者Thread.interrupted()来检测线程的中断标志是否被置位。这两个方法的区别是Thread.currentThread().isInterrupted()是线程对象的方法,调用它后不清除线程中断标志位;而Thread.interrupted()是一个静态方法,调用它会清除线程中断标志位。
中断非阻塞线程
比如,让主线程休眠一秒钟,保证线程1执行一段时间,然后在主线程中中断线程1,在线程1中用过检测标志位来查看线程是否该中断。如果中断了,保存计算结果。
public class InterruptTest2 {
public static void main(String[] args) {
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (!Thread.currentThread().isInterrupted()){
System.out.println("线程还在执行...");
for (; i < 1000000000; i++) {
}
}
if (Thread.currentThread().isInterrupted()){
System.out.println(i);
}
}
});
thread2.start();
try {
Thread.sleep(1000);
thread2.interrupt();
System.out.println("主线程中断线程1");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
中断阻塞线程
对于一些可阻塞的方法,比如sleep(),wait(),join()等方法,如果我们不用中断机制,而是自己去设置取消方法,那么当线程永久堵塞时,也就不能去检测标志位,那么线程就永远停不下来了。但是通过interrupt,这些可阻塞方法在JVM内部会检测interrupt标志位,当发现中断时,会抛出InterruptedException异常,来终止阻塞方法。
public class InterruptTest {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+"正在执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//由于调用sleep()方法清除状态标志位 所以这里需要再次重置中断标志位 否则线程会继续运行下去。保留中断证据
Thread.currentThread().interrupt();
e.printStackTrace();
}
if (Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+"被中断了");
}
}
}
});
thread.start();
try {
System.out.println("主线程睡眠");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程中断其他线程");
thread.interrupt();
//线程一旦被中断,isInterrupted()方法便会返回true,而一旦sleep()方法抛出异常,它将清空中断标志,此时isInterrupted()方法将返回false。
System.out.println("是否被中断1:"+thread.isInterrupted());
System.out.println("主线程执行完成");
}
}
对于非阻塞方法的中断,也可以通过抛出InterruptedException异常来传递中断。
不可中断线程
有一种情况是线程不能被中断的,比如调用synchronized关键字和reentrantLock.lock()获取锁的过程。输入和输出流类会阻塞等待 I/O 完成,但是它们不抛出 InterruptedException,而且在被中断的情况下也不会退出阻塞状态。
public class InterruptThreadTest5 {
public void deathLock(Object lock1, Object lock2) {
try {
synchronized (lock1) {
System.out.println(Thread.currentThread().getName()+ " is running");
// 让另外一个线程获得另一个锁
Thread.sleep(10);
// 造成死锁
synchronized (lock2) {
System.out.println(Thread.currentThread().getName());
}
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+ " is interrupted");
e.printStackTrace();
}
}
public static void main(String [] args) {
final InterruptThreadTest5 itt = new InterruptThreadTest5();
final Object lock1 = new Object();
final Object lock2 = new Object();
Thread t1 = new Thread(new Runnable(){
public void run() {
itt.deathLock(lock1, lock2);
}
},"A");
Thread t2 = new Thread(new Runnable(){
public void run() {
itt.deathLock(lock2, lock1);
}
},"B");
t1.start();
t2.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 中断线程t1、t2
t1.interrupt();
t2.interrupt();
}
}