MST
星途 面试题库

面试题:Java固定数目线程池中的线程复用与资源释放

阐述Java固定数目线程池中线程复用的原理。当线程池中的线程长时间处理任务后,如何合理地进行资源(如数据库连接、文件句柄等)释放,以避免资源泄漏?请结合实际代码示例说明。
22.2万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

Java固定数目线程池中线程复用原理

在Java的固定数目线程池(如ThreadPoolExecutor通过Executors.newFixedThreadPool(int nThreads)创建的线程池)中,线程复用基于以下机制:

  1. 任务队列:当提交新任务时,如果线程池中有空闲线程,任务会被分配给空闲线程执行。若所有线程都在忙碌,任务会被放入任务队列(BlockingQueue)中等待。
  2. 线程生命周期管理:线程池创建时会初始化固定数量的线程。这些线程启动后,会不断从任务队列中获取任务并执行。当一个任务执行完毕,线程不会销毁,而是再次从任务队列中获取新任务,从而实现线程复用。

资源释放避免资源泄漏

为了避免长时间处理任务后资源(如数据库连接、文件句柄等)泄漏,需要确保在任务完成后正确释放资源。可以使用try - finally块或Java 7引入的try - with - resources语句(适用于实现了AutoCloseable接口的资源)。

以下是使用数据库连接(以JDBC为例)的代码示例:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolResourceManagement {
    private static final String URL = "jdbc:mysql://localhost:3306/mydb";
    private static final String USER = "root";
    private static final String PASSWORD = "password";

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 5; i++) {
            executorService.submit(new DatabaseTask());
        }
        executorService.shutdown();
    }

    static class DatabaseTask implements Runnable {
        @Override
        public void run() {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                connection = DriverManager.getConnection(URL, USER, PASSWORD);
                String sql = "SELECT * FROM users";
                preparedStatement = connection.prepareStatement(sql);
                resultSet = preparedStatement.executeQuery();
                while (resultSet.next()) {
                    System.out.println(resultSet.getString("username"));
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (resultSet != null) resultSet.close();
                    if (preparedStatement != null) preparedStatement.close();
                    if (connection != null) connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在上述代码中,DatabaseTask类实现了Runnable接口,在run方法中进行数据库操作。通过try - finally块,确保无论数据库操作是否成功,都能正确关闭ResultSetPreparedStatementConnection,从而避免资源泄漏。

如果使用Java 7及以上版本,对于实现了AutoCloseable接口的资源,可以使用try - with - resources语句,代码会更加简洁:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolResourceManagementTryWithResources {
    private static final String URL = "jdbc:mysql://localhost:3306/mydb";
    private static final String USER = "root";
    private static final String PASSWORD = "password";

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 5; i++) {
            executorService.submit(new DatabaseTask());
        }
        executorService.shutdown();
    }

    static class DatabaseTask implements Runnable {
        @Override
        public void run() {
            String sql = "SELECT * FROM users";
            try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
                 PreparedStatement preparedStatement = connection.prepareStatement(sql);
                 ResultSet resultSet = preparedStatement.executeQuery()) {
                while (resultSet.next()) {
                    System.out.println(resultSet.getString("username"));
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

在这个示例中,try - with - resources语句会自动在代码块结束时关闭ConnectionPreparedStatementResultSet,进一步简化了资源管理并确保资源不会泄漏。