网络连接出现问题,可能会出现两种情况:
- 线程资源长期被占用着。
- 已建立的网络连接,无法被正常释放。
这些情况进而会导致服务器资源被白白占用,客户端的过来请求也会堆积的越来越多,无法被处理。
这时为了避免事态进一步恶化,我们必须想办法释放线程资源。那么要怎么做才能释放线程资源呢?
往往要给线程结束工作时的一个处理当前工作机制或者给予处理完成点工作的时间才行。如图:
既要保证线程灵活的切换运行状态,又要保证线程优雅的处理完当前任务,就是两阶段终止模式(Two-phase Termination)的核心思想。
综上所述两阶段终止模式的两个阶段总结起来分别为:
- 发出信号,告知正在运行的线程将被终止。
- 接收到此信号的线程,做完善后工作,停止运行。
线程阻塞本质上是一段代码执行时间过长,那它自然也没有办法感知到标记位的改变,从而优雅的终止线程。那么当一个线程发生阻塞时我们又该怎么去终止它呢?如果线程发生阻塞想要停止,就不太符合两阶段终止的思想了,属于一种暴力解决方式,这里提供一段代码作为一种思路。
public class ThreadExecutor {
/**
* 执行线程
*/
private Thread executeThread;
/**
* 运行状态
*/
private volatile boolean isRunning = false;
/**
*
* @param task 发生阻塞的线程任务
*/
public void execute(Runnable task) {
executeThread = new Thread(() -> {
Thread childThread = new Thread(task);
// 子线程设置为守护线程
childThread.setDaemon(true);
childThread.start();
try {
// 强行执行子线程,使其进入休眠状态
childThread.join();
isRunning= true;
} catch (InterruptedException e) {
//e.printStackTrace();
}
});
executeThread.start();
}
/**
*
* @param mills 强制结束任务的时长阈值
*/
public void shutdown(long mills) {
long currentTime = System.currentTimeMillis();
while (!isRunning) {
if ((System.currentTimeMillis() - currentTime) >= mills) {
System.out.println("任务超时,需要结束他!");
executeThread.interrupt();
break;
}
}
isRunning = false;
}
public static void main(String[] args) {
ThreadExecutor executor = new ThreadExecutor();
long start = System.currentTimeMillis();
executor.execute(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
executor.shutdown(1000);
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
在 ThreadPoolExecutor 的 shutdownNow
方法运用到了两阶段中止模式,shutdown 方法执行后,就会拒绝接收新的任务,但是会等待线程池中正在执行的任务和已经进入阻塞队列的任务都执行完之后,才最终关闭线程池。