线程中断总结
1.线程中断API
理解线程中断的本质,可以从"如果没有线程中断机制,会有什么后果"的角度考虑: 因可能引发数据不同步以及资源无法回收问题,通过stop暴力停止线程的方法已被废弃;线程被CPU调度后会独立发展和自治, 无法解决如线程长期阻塞后无法恢复等问题。
因此,中断机制是为了引入从线程外部进行干预的能力。
"没有任何语言方面的需求要求一个被中断的程序应该终止。中断一个线程只是为了引起该线程的注意,被中断线程可以决定如何应对中断 "
线程被中断时, 在JVM中该线程会收到一个中断信号(并将线程标记为中断状态),由线程自己决定是否处理中断。常用线程API中,部分阻塞(wait/sleep/join/future等)会响应中断——抛出中断异常并清理中断状态,部分会直接忽略中断。
Java为线程中断提供了3个API:
// 静态方法,返回当前线程是否中断&&清理中断状态
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
// 返回线程的中断状态
public boolean isInterrupted() {
return isInterrupted(false);
}
// 将线程标记为中断状态
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
2.sleep和wait被中断
当线程因wait和sleep()方法阻塞后,被中断时会抛出InterruptedException中断异常,以下通过案例进行介绍。
//为了简化Demo代码的try-catch逻辑,添加一个工具类
public class ThreadUtils {
public static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
sleep案例:
public class SleepDemo {
public static void main(String[] args) {
// subThread线程休眠60s
Thread subThread = new Thread(() -> ThreadUtils.sleep(60 * 1000));
subThread.start();
ThreadUtils.sleep(1000);
// main线程中调用subThread的interrupt方法,打断subThread线程
subThread.interrupt();
}
}
运行结果如下:
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.testthread.ThreadUtils.sleep(ThreadUtils.java:10)
at com.testthread.SleepDemo.lambda$main$0(SleepDemo.java:9)
at java.lang.Thread.run(Thread.java:745)
Process finished with exit code 0
wait案例:
public class WaitDemo {
private static Object lock = new Object();
public static void main(String[] args) {
Thread subThread = new Thread(() -> {
synchronized (lock) {
try {
// subThread线程调用wait方法阻塞
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
subThread.start();
ThreadUtils.sleep(1000);
// main线程中调用subThread的interrupt方法,打断subThread线程
subThread.interrupt();
}
}
运行结果如下所示:
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at com.testthread.WaitDemo.lambda$main$0(WaitDemo.java:14)
at java.lang.Thread.run(Thread.java:745)
顺便提一下:sleep是抱着锁休眠的,wait阻塞前会释放锁。
3.IO中断
常用的IO操作不会响应中断,以下通过案例进行介绍。
public class IODemo {
private static Object lock = new Object();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (lock) {
Socket socket = new Socket();
try {
socket.connect(new InetSocketAddress("127.0.0.1", 9999));
InputStream inputStream = socket.getInputStream();
System.out.println("[Thread-1] Begin to read...");
// 因没有可读数据,thread1持续处于阻塞状态
inputStream.read();
System.out.println("[Thread-1] Read finish.");
} catch (IOException e) {
e.printStackTrace();
}
}
});
thread1.start();
ThreadUtils.sleep(1000);
Thread thread2 = new Thread(() -> {
// thread1获取锁后阻塞且不释放锁,thread2因获取不到锁,处于阻塞状态
synchronized (lock) {
System.out.println("[Thread-2] Begin to exec subThread-2...");
}
});
thread2.start();
System.out.println("[Main] Begin to set thread interrupt...");
// main线程中调用thread1的interrupt方法,打断thread1线程
thread1.interrupt();
System.out.println("[Main] Success to set thread interrupt.");
}
}
程序持续处于阻塞状态,运行结果如下所示:
[Thread-1] Begin to read...
[Main] Begin to set thread interrupt...
[Main] Success to set thread interrupt.
此案例还说明了,IO阻塞时,不会释放当前线程占有的锁。
4.FutureTask.get()阻塞中断
FutureTask提供了两个阻塞获取结果的方法:get(long timeout, TimeUnit unit)
和get()
. 前者超时后会抛出TimeoutException异常,从阻塞中恢复,后者会一直阻塞; 二者对中断的响应处理相同(抛出中断异常),以get()
为例进行介绍。
public class FutureTaskDemo {
private static ExecutorService executorService = Executors.newSingleThreadExecutor();
public static void main(String[] args) {
FutureTask futureTask = new FutureTask(() -> {
// futureTask阻塞10秒
ThreadUtils.sleep(1000 * 10);
return new Object();
});
executorService.submit(futureTask);
Thread subThread = new Thread(() -> {
try {
// subThread获取futureTask的运行结果而阻塞
futureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
subThread.start();
ThreadUtils.sleep(1000);
// main线程中调用subThread的interrupt方法,打断subThread线程
subThread.interrupt();
}
}
运行结果如下所示:
java.lang.InterruptedException
at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:404)
at java.util.concurrent.FutureTask.get(FutureTask.java:191)
at com.testthread.FutureTaskDemo.lambda$main$1(FutureTaskDemo.java:21)
at java.lang.Thread.run(Thread.java:745)
5.Locksupport.park()被中断
Locksupport.park()阻塞的线程被中断时,会被唤醒并标记为中断状态,而不会抛出异常。案例如下所示:
public class LockSupportDemo {
public static void main(String[] args) {
Thread subThread = new Thread(() -> {
System.out.println("[subThread-begin] state is "+Thread.currentThread().isInterrupted());
// 调用LockSupport.park()阻塞当前线程
LockSupport.park();
System.out.println("[subThread-end] state is "+Thread.currentThread().isInterrupted());
});
subThread.start();
ThreadUtils.sleep(1000);
System.out.println("[Main] begin to interrupt subThread.");
// main线程中调用subThread的interrupt方法,打断subThread线程
subThread.interrupt();
// LockSupport.unpark(subThread);
System.out.println("[Main] Success to interrupt subThread.");
}
}
运行结果如下所示:
[subThread-begin] state is false
[Main] begin to interrupt subThread.
[Main] Success to interrupt subThread.
[subThread-end] state is true
当线程因LockSupport.park()阻塞,当该线程被中断时,线程会被唤醒并标记为中断状态(JUC中经常使用)。
6.JUC其他API
JUC常见的AIP还有Lock、Condition、BlockingQueue等。
6.1 Lock
public interface Lock {
void lockInterruptibly() throws InterruptedException;
void lock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
}
Lock中lock()不响应中断,lockInterruptibly响应中断(抛出InterruptedException异常)。
6.2 Condition
public interface Condition {
void awaitUninterruptibly();
void await() throws InterruptedException;
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
}
Condition中:awaitUninterruptibly不响应中断,其他均响应中断(抛出InterruptedException异常).
6.3 BlockingQueue
public interface BlockingQueue<E> extends Queue<E> {
void put(E e) throws InterruptedException;
E take() throws InterruptedException;
boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException;
E poll(long timeout, TimeUnit unit) throws InterruptedException;
}
BlockingQueue中所有阻塞方法均响应中断(抛出InterruptedException异常).