1. 优雅终止线程的设计模式
思考:在一个线程 T1 中如何优雅的终止线程 T2?
错误思路1:使用线程对象的 stop() 方法停止线程
stop 方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释
放锁, 其它线程将永远无法获取锁 。
错误思路2:使用 System.exit(int) 方法停止线程
目的仅是停止一个线程,但这种做法会让整个程序都停止
正确思路:两阶段终止模式
1.1 两阶段终止(Two-phase Termination)模式——优雅的终止线程
两阶段终止(Two-phase Termination)模式是一种用于优雅终止线程的设计模式。该模式的基本思想是通过两个阶段来终止线程,第一个阶段是发送终止请求,第二个阶段是等待线程终止。
思考:第一阶段,在Java中如何发送终止请求?
回顾一下Java线程的生命周期:
Java 线程进入终止状态的前提是线程进入 RUNNABLE 状态,而实际上线程也可能处在休眠状 态,也就是说,我们要想终止一个线程,首先要把线程的状态从休眠状态转换到 RUNNABLE 状态。
利用java线程中断机制的interrupt() 方法,可以让线程从休眠状态转换到RUNNABLE 状态。
思考:第二阶段,线程转换到 RUNNABLE 状态之后,我们如何再将其终止呢?
RUNNABLE 状态转换到终止状态,优雅的方式是让 Java 线程自己执行完 run() 方法,所以一般
我们采用的方法是设置一个标志位,然后线程会在合适的时机检查这个标志位,如果发现符合终止条件,则自动退出 run() 方法。
综合上面这两点,我们能总结出终止指令,其实包括两方面内容:
interrupt() 方法和线程终止的
标志位。
两阶段终止模式可以带来多种好处,例如:
1.
优雅终止:两阶段终止模式可以优雅地终止线程,避免突然终止线程带来的副作用。
2.
安全性:两阶段终止模式可以在线程终止前执行必要的清理工作,以确保程序的安全性和稳定性。
3.
灵活性:两阶段终止模式可以根据具体情况灵活地设置终止条件和清理工作
1.2 使用场景
两阶段终止模式适用于需要优雅终止线程的场景,例如:
1. 服务器应用程序:
在服务器应用程序中,需要处理大量的请求和数据,并且需要在终止时正确地保存和释放资源,以避免数据丢失和资源泄漏。
2. 大规模并发系统
:在大规模并发系统中,线程数量可能非常多,并且需要在终止时正确地关闭和释放所有的线程 和资源。
3. 定时任务系统:
在定时任务系统中,需要在任务执行完毕后正确地终止任务线程,并清理相关资源。
4. 数据处理系统:
在数据处理系统中,需要在处理完所有数据后正确地终止线程,并清理相关资源。
5. 消息订阅系统:
在消息订阅系统中,需要在订阅结束后正确地终止订阅线程,并清理相关资源。
总之,两阶段终止模式适用于需要优雅终止线程的各种场景,可以提高程序的可靠性和可维护性。在应用该模式时,需要注意正确设计终止条件和清理工作,以避免出现线程安全问题和资源泄漏问题。
利用两阶段终止模式设计优雅终止监控操作
在多线程程序中,如果有一些线程需要执行长时间的监控或者轮询操作,可以使用两阶段终止模式来 终止这些线程的执行。使用两阶段终止模式可以保证监控线程在执行终止操作时能够安全地释放资源 和退出线程。同时,该模式还可以保证监控线程在终止前能够完成必要的清理工作,从而避免资源泄 露和其他问题。
/**
* 用两阶段终止模式终止监控操作
*/
public class MonitorThread extends Thread {
//在监控线程中添加一个volatile类型的标志变量,用于标识是否需要终止线程的执行
private volatile boolean terminated = false;
public void run() {
while (!Thread.interrupted()&&!terminated) {
// 执行监控操作
System.out.println("监控线程正在执行监控操作...");
try {
Thread.sleep(5000);
// 卡死
} catch (InterruptedException e) {
// 检查中断状态
System.out.println("监控线程被中断,准备退出...");
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
// 执行清理操作
System.out.println("监控线程正在执行清理操作...");
releaseResources();
}
public void terminate() {
//设置标志变量为true,并等待一段时间
terminated = true;
try {
join(5000); // 等待5秒钟,期间监控线程会检查terminated的状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void releaseResources() {
// 释放资源和进行必要的清理工作
System.out.println("监控线程正在释放资源和进行必要的清理工作...");
}
public static void main(String[] args) throws InterruptedException {
MonitorThread thread = new MonitorThread();
//启动监控线程
thread.start();
//主线程休眠期间,监控线程在执行监控操作
Thread.sleep(10000);
//终止监控线程
//thread.terminate();
thread.interrupt();
Thread.sleep(100000);
}
在使用两阶段终止模式终止线程的同时,还可以结合中断机制实现更加可靠和灵活的线程终止操作。 使用中断机制可以让线程在终止前及时响应中断请求,并退出线程的阻塞状态。同时,还可以通过检 查中断状态和标志变量的状态来判断是否需要终止线程的执行,从而实现更加可靠和灵活的线程终止操作.
/**
* 用两阶段终止模式终止监控操作
*/
public class MonitorThread2 extends Thread {
//在监控线程中添加一个volatile类型的标志变量,用于标识是否需要终止线程的执行
private volatile boolean terminated = false;
public void run() {
while (!Thread.interrupted()&&!terminated) {
// 执行监控操作
System.out.println("监控线程正在执行监控操作...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("监控线程被中断,准备退出...");
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
// 执行清理操作
System.out.println("监控线程正在执行清理操作...");
releaseResources();
}
public void terminate() {
//设置标志变量为true,并等待一段时间
terminated = true;
try {
join(5000); // 等待5秒钟,期间监控线程会检查terminated的状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void releaseResources() {
// 释放资源和进行必要的清理工作
System.out.println("监控线程正在释放资源和进行必要的清理工作...");
}
public static void main(String[] args) throws InterruptedException {
MonitorThread2 thread = new MonitorThread2();
//启动监控线程
thread.start();
//主线程休眠期间,监控线程在执行监控操作
Thread.sleep(10000);
//为监控线程设置中断标志位
thread.interrupt();
//终止监控线程
//thread.terminate();
Thread.sleep(100000);
}
}
如何优雅地终止线程池
Java 领域用的最多的还是线程池,而不是手动地创建线程。那我们该如何优雅地终止线程池呢?
线程池提供了两个终止线程池的方法:
shutdown()方法会停止线程池接受新的任务,并等待线程池中的所有任务执行完毕
,然后关闭线程池。在调用 shutdown()方法后,线程池不再接受新的任务,但是会将任务队列中的任务继续执行直到队列为空。如果线程池 中的任务正在执行,但是还没有执行完毕,线程池会等待所有任务执行完毕后再关闭线程池。
shutdownNow()方法会停止线程池接受新的任务,并尝试中断正在执行的任务
,然后关闭线程池。在调用 shutdownNow()方法后,线程池不再接受新的任务,同时会
中断正在执行的任务并返回一个未执行的任务列 表。
该方法会调用每个任务的interrupt()方法尝试中断任务的执行,但是并不能保证任务一定会被中断,因为任务可以选择忽略中断请求.
/**
* 如何优雅地终止线程池
*/
public class ThreadPoolDemo {
public static void main(String[] args) th