MST

星途 面试题库

面试题:Java BIO与线程池结合应用中,如何处理线程池满的情况

在使用Java BIO与线程池结合开发服务器程序时,如果线程池已满,新的客户端连接请求不断到来,应该如何优雅地处理这种情况,以避免系统崩溃,并尽可能保证服务的可用性,描述具体的实现思路和涉及到的关键代码片段。
37.3万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

实现思路

  1. 设置合理的拒绝策略:当线程池已满,无法处理新的任务时,需要选择合适的拒绝策略来处理这些任务,而不是直接丢弃。
  2. 排队等待:可以使用一个额外的队列,如BlockingQueue,将无法立即处理的客户端连接请求暂存起来,待线程池有空闲线程时再处理。
  3. 动态调整线程池大小:根据系统的负载情况,动态调整线程池的大小,以适应不断变化的请求数量。

关键代码片段

  1. 创建线程池并设置拒绝策略
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

// 创建一个有界队列
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(100);
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
        10, // 核心线程数
        50, // 最大线程数
        10, // 线程存活时间
        TimeUnit.SECONDS,
        queue,
        new ThreadPoolExecutor.CallerRunsPolicy()); // 使用CallerRunsPolicy拒绝策略

在上述代码中,CallerRunsPolicy拒绝策略会让提交任务的线程(通常是主线程)来执行任务,这样可以保证任务不会被丢弃,同时也能起到一定的限流作用。

  1. 处理客户端连接请求
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    private static final int PORT = 8080;
    private final ThreadPoolExecutor executor;

    public Server(ThreadPoolExecutor executor) {
        this.executor = executor;
    }

    public void start() {
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("Server started on port " + PORT);
            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println("New client connected: " + clientSocket);
                // 将客户端连接处理任务提交到线程池
                executor.submit(() -> {
                    try {
                        // 处理客户端连接的具体逻辑
                        handleClient(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            clientSocket.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void handleClient(Socket clientSocket) throws IOException {
        // 具体的客户端处理逻辑,例如读取和写入数据
    }
}

start方法中,通过ServerSocket监听客户端连接,一旦有新连接,就将处理任务提交到线程池。如果线程池已满,根据设置的拒绝策略处理任务。

  1. 动态调整线程池大小
// 根据系统负载动态调整线程池大小
public void adjustThreadPoolSize(int newCorePoolSize, int newMaximumPoolSize) {
    executor.setCorePoolSize(newCorePoolSize);
    executor.setMaximumPoolSize(newMaximumPoolSize);
}

可以根据系统的负载情况(如CPU使用率、任务队列长度等)调用adjustThreadPoolSize方法动态调整线程池的核心线程数和最大线程数。