设计方案
- 自定义任务类:
- 创建一个实现
Runnable
接口的自定义任务类,例如ResourceTask
。在这个类中封装数据库连接、文件句柄等资源的操作。
- 在
ResourceTask
类中添加获取和释放资源的方法,例如:
public class ResourceTask implements Runnable {
private Connection connection;
private FileHandle fileHandle;
public ResourceTask(Connection connection, FileHandle fileHandle) {
this.connection = connection;
this.fileHandle = fileHandle;
}
@Override
public void run() {
// 执行数据库和文件操作
try {
// 数据库操作
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM some_table");
// 文件操作
fileHandle.write("Some data");
} catch (SQLException | IOException e) {
e.printStackTrace();
}
}
public void releaseResources() {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (fileHandle != null) {
try {
fileHandle.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 线程池关闭逻辑:
- 获取线程池对象,例如
ThreadPoolExecutor executor
。
- 调用
executor.shutdown()
方法,启动有序关闭,此时线程池不再接受新任务,但会继续执行已提交的任务。
- 使用
while
循环结合executor.awaitTermination
方法等待所有任务执行完成:
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow();
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("Pool did not terminate");
}
}
} catch (InterruptedException ie) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
- 在任务执行完成后(即
awaitTermination
返回true
),遍历线程池中的任务队列,对每个ResourceTask
调用releaseResources
方法释放资源:
List<Runnable> tasks = executor.getQueue();
for (Runnable task : tasks) {
if (task instanceof ResourceTask) {
((ResourceTask) task).releaseResources();
}
}
不同线程池状态转换下可能面临的问题及解决方案
- RUNNING状态:
- 问题:在
RUNNING
状态下调用shutdown
或shutdownNow
方法时,可能会有新任务不断提交进来,导致任务队列持续增长。
- 解决方案:在调用
shutdown
或shutdownNow
之前,通过外部控制逻辑停止新任务的提交,例如设置一个标志位,在任务提交处检查该标志位。
- SHUTDOWN状态:
- 问题:可能存在任务执行时间过长,导致
awaitTermination
超时,无法正常关闭线程池。
- 解决方案:可以在任务设计时,合理设置任务执行的超时时间。如果任务执行时间过长,可以通过
Future
类的cancel
方法尝试取消任务。另外,适当延长awaitTermination
的等待时间,同时监控任务执行情况,避免长时间等待。
- STOP状态:
- 问题:调用
shutdownNow
进入STOP
状态时,正在执行的任务可能被中断,导致资源没有正确释放。
- 解决方案:在任务的
run
方法中,使用try - catch
块捕获InterruptedException
,在捕获到异常时,正确释放资源。例如在ResourceTask
的run
方法中:
@Override
public void run() {
try {
// 执行数据库和文件操作
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM some_table");
fileHandle.write("Some data");
} catch (SQLException | IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
releaseResources();
Thread.currentThread().interrupt();
}
}
- TERMINATED状态:
- 问题:一般情况下,进入
TERMINATED
状态表示所有任务已执行完成,但可能存在资源未释放的情况(如前面提到的任务执行超时等导致资源未释放)。
- 解决方案:在进入
TERMINATED
状态后,再次检查并释放资源,如上述遍历任务队列释放资源的操作。同时,在应用层面添加资源监控机制,定期检查是否有未释放的资源。