Java 线程池满了怎么阻塞主线程的方案

在现代 Java 应用程序中,使用线程池来管理和调度任务是非常普遍的。然而,在某些情况下,线程池可能会因为任务数量过多而达到最大容量。这时,如果不处理好,主线程可能会继续执行并导致资源浪费或者出现错误。因此,我们需要一种阻塞主线程的方案,以确保所有任务都能被正确处理。

一、问题分析

Java 的ThreadPoolExecutor类提供了线程池的基本实现。当线程池中的线程数达到设定的核心线程数,并且任务数超过了任务队列的最大容量时,新的任务会被拒绝。这种情况下,主线程继续执行可能导致任务丢失或者资源浪费。因此,主线程需要在任务队列满时进行阻塞。

二、解决方案

我们可以通过自定义ThreadPoolExecutor来实现这一需求。通过重写afterExecute()方法,我们可以在确保线程池满时将主线程阻塞起来。以下是一个示例实现。

1. 自定义线程池

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class BlockingThreadPoolExecutor extends ThreadPoolExecutor {
    public BlockingThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, 
                                      BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if (getActiveCount() >= getMaximumPoolSize() && getQueue().remainingCapacity() == 0) {
            System.out.println("线程池已满,阻塞主线程...");
            try {
                // 主线程阻塞,直到有任务完成
                synchronized (this) {
                    wait();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    public synchronized void notifyMainThread() {
        notifyAll();
    }
}

2. 使用自定义线程池

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args) {
        BlockingThreadPoolExecutor executor = new BlockingThreadPoolExecutor(
            3, 
            3, 
            0L, 
            TimeUnit.MILLISECONDS, 
            new ArrayBlockingQueue<>(5)
        );

        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executor.execute(() -> {
                try {
                    System.out.println("执行任务:" + taskId);
                    Thread.sleep(1000); // 模拟任务执行
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    executor.notifyMainThread(); // 在任务完成时通知主线程
                }
            });
        }

        executor.shutdown();
    }
}

三、总结

通过上述自定义线程池的方案,我们能够有效地控制主线程的执行。当线程池满时,主线程会被阻塞,直到有任务完成,确保所有任务都被顺利处理。这种方式避免了资源的浪费和任务的丢失。

在实际项目中,可以根据业务需求进一步优化上述代码,并考虑异常处理和线程安全等问题。总的来说,自定义线程池的方案为处理线程池满状态提供了有效的解决方法,确保了系统的稳定性和可用性。