前言
最近,遇到一个情况,引用的一些外部的cloud的包在调用的时候的时候报错了,看报错的内容是
The current thread was interrupted at xxxxx
后面就是堆栈信息, 在代码中是try catch(Exception e)
这段代码的,但是这个线程最后是阻断了,所以无法继续运行下去
于是...
我们来总结一下,关闭线程的方法,和如何优雅的关闭线程
1.interrupt
首先我们来看看我们遇到的线程中断
public static void test() throws InterruptedException {
Thread t = new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println("process i=" + i + ",interrupted:" + Thread.currentThread().isInterrupted());
}
});
t.start();
Thread.sleep(1);
t.interrupt();
}
以上的代码执行结果:
...
process i=55,interrupted:false
process i=56,interrupted:false
process i=57,interrupted:true
process i=58,interrupted:true
process i=59,interrupted:true
...
在57的时候,interrupt的标识变成true了,但是线程依旧在打印,
但是如果我们改成:
public static void test() throws InterruptedException {
Thread t = new Thread(() -> {
for (int i = 0; i < 100; i++) {
if (Thread.interrupted()) {
System.out.println("线程已中断,退出执行");
break;
}
System.out.println("process i=" + i + ",interrupted:" + Thread.currentThread().isInterrupted());
}
});
t.start();
Thread.sleep(1);
t.interrupt();
}
则会得到以下的结果:
process i=49,interrupted:false
process i=50,interrupted:false
process i=51,interrupted:false
线程已中断,退出执行
由此我们可以得到结论,Thread的interrupt()是不会直接关闭线程的,相当于标记整个线程需要阻断不能再执行下去了
我们来看看线程池的
/**
* Initiates an orderly shutdown in which previously submitted
* tasks are executed, but no new tasks will be accepted.
* Invocation has no additional effect if already shut down.
*
* <p>This method does not wait for previously submitted tasks to
* complete execution. Use {@link #awaitTermination awaitTermination}
* to do that.
*
* @throws SecurityException {@inheritDoc}
*/
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN); // 1. 把线程池的状态设置为 SHUTDOWN
interruptIdleWorkers(); // 2. 把空闲的工作线程置为中断
onShutdown(); // 3. 一个空实现,暂不用关注
} finally {
mainLock.unlock();
}
tryTerminate();
}
注释可以看到,该方法有序的关闭之前的任务,且不接受新的任务,如果关闭了也不会产生其他影响。
执行shutdown后知道任务数变成0,再进行terminated进行销毁
方式2使用shutdownNow
/**
* Attempts to stop all actively executing tasks, halts the
* processing of waiting tasks, and returns a list of the tasks
* that were awaiting execution. These tasks are drained (removed)
* from the task queue upon return from this method.
*
* <p>This method does not wait for actively executing tasks to
* terminate. Use {@link #awaitTermination awaitTermination} to
* do that.
*
* <p>There are no guarantees beyond best-effort attempts to stop
* processing actively executing tasks. This implementation
* cancels tasks via {@link Thread#interrupt}, so any task that
* fails to respond to interrupts may never terminate.
*
* @throws SecurityException {@inheritDoc}
*/
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP); // 1:把线程池设置为STOP
interruptWorkers(); // 2.中断工作线程
tasks = drainQueue(); // 3.把线程池中的任务都 drain 出来
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
该方法和shutdown不同在于,直接停止所有当前的线程,更暴力一些
应用
在实际引用中,我们在滚动更新、部署项目的时候会使用到关闭线程的方式,当然根据上面的我们也可以知道,类似于在k8s上更新项目,最好的方式当然是shutdown等待所有的线程都执行完之后再把任务结束,从而达到递进式的更新