固定大小线程池通过限制并发线程数来控制资源使用,适用于服务器并发处理、批处理、资源受限及计算密集型任务;其核心优势是避免系统过载并提升稳定性。但Executors.newFixedThreadPool()默认使用无界队列,可能导致内存溢出。解决方案是直接使用ThreadPoolExecutor创建线程池,指定有界队列(如ArrayBlockingQueue)和合适的拒绝策略,从而在保证性能的同时规避风险。

在Java里,要创建一个固定大小的线程池,最直接也最常用的方式就是通过
Executors.newFixedThreadPool()
创建固定大小线程池,我们可以这样做:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class FixedThreadPoolDemo {
public static void main(String[] args) {
// 创建一个固定大小为3的线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交10个任务
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println(Thread.currentThread().getName() + " 正在执行任务 " + taskId);
try {
Thread.sleep(1000); // 模拟任务执行时间
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 重新设置中断状态
System.err.println(Thread.currentThread().getName() + " 的任务 " + taskId + " 被中断。");
}
});
}
// 关闭线程池,等待所有任务完成
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("线程池未在指定时间内关闭,尝试强制关闭。");
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
System.out.println("所有任务提交完毕,线程池已关闭。");
}
}这段代码里,我们用
Executors.newFixedThreadPool(3)
固定大小线程池,顾名思义,它的核心优势在于对并发线程数的严格控制。在我日常开发中,这简直就是处理一些特定场景的“利器”。
立即学习“Java免费学习笔记(深入)”;
首先,资源可控性是它最显著的特点。你想想看,如果你的服务器CPU是四核的,你非要启动几百个线程去跑计算密集型任务,那结果多半是上下文切换的开销把CPU都占满了,实际工作效率反而下降。固定大小线程池就能帮你把并发数限制在一个合理的范围,比如跟CPU核心数相近,这样就能更好地利用CPU资源,避免因为线程过多导致的系统性能急剧下降,甚至OOM(内存溢出)的风险。
其次,它能提供更可预测的性能。由于线程数量恒定,每次任务的执行开销,包括线程创建、销毁的成本,都被摊平了。系统不会因为任务量的波动而频繁地创建或销毁线程,从而减少了不必要的开销,使得整体响应时间和吞吐量在一个相对稳定的区间。
至于适用场景,我个人觉得它在以下几个地方特别出彩:
在我看来,选择固定大小线程池,往往是我们在“性能”和“稳定性”之间找到一个平衡点的明智之举。
Executors.newFixedThreadPool()
当我们调用
Executors.newFixedThreadPool(int nThreads)
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)而
newFixedThreadPool
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());这里有几个关键点:
corePoolSize
maximumPoolSize
nThreads
nThreads
nThreads
keepAliveTime
0L
corePoolSize
maximumPoolSize
new LinkedBlockingQueue<Runnable>()
LinkedBlockingQueue
Integer.MAX_VALUE
潜在风险:
这个无界队列意味着什么呢?如果任务提交的速度远超线程池处理任务的速度,那么所有提交的任务都会被堆积到这个
LinkedBlockingQueue
OutOfMemoryError
想象一下,你的服务突然涌入大量请求,每个请求都提交一个任务到这个固定大小的线程池。如果任务处理得慢,队列就会像一个无底洞一样,不停地吞噬内存,直到你的应用程序崩溃。而且,由于没有拒绝策略(默认的
AbortPolicy
所以,尽管
newFixedThreadPool
鉴于
Executors.newFixedThreadPool()
ThreadPoolExecutor
这里,我们可以自己指定一个有界队列,并配置合适的拒绝策略。这能让我们在系统资源耗尽之前,对过多的任务进行处理,比如直接拒绝、记录日志或者降级处理。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.Executors; // 用于创建ThreadFactory
public class CustomFixedThreadPoolDemo {
public static void main(String[] args) {
// 核心线程数和最大线程数都设为3,模拟固定大小
int corePoolSize = 3;
int maximumPoolSize = 3;
// 线程空闲时间,这里设为0,因为是固定大小,线程不会被回收
long keepAliveTime = 0L;
TimeUnit unit = TimeUnit.MILLISECONDS;
// 使用一个有界队列,容量为10
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);
// 自定义拒绝策略:当队列和线程池都满时,直接抛出RejectedExecutionException
// 也可以选择CallerRunsPolicy(调用者执行)、DiscardPolicy(直接丢弃)或DiscardOldestPolicy(丢弃最老的)
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
// 创建自定义的ThreadPoolExecutor
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
Executors.defaultThreadFactory(), // 使用默认的线程工厂
handler
);
// 提交20个任务,观察有界队列和拒绝策略的效果
for (int i = 0; i < 20; i++) {
final int taskId = i;
try {
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 正在执行任务 " + taskId);
try {
Thread.sleep(1000); // 模拟任务执行时间
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println(Thread.currentThread().getName() + " 的任务 " + taskId + " 被中断。");
}
});
} catch (Exception e) {
System.err.println("任务 " + taskId + " 提交失败: " + e.getMessage());
}
}
// 关闭线程池
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("线程池未在指定时间内关闭,尝试强制关闭。");
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
System.out.println("所有任务提交完毕,线程池已关闭。");
}
}在这个自定义的例子中,我们:
corePoolSize
maximumPoolSize
ArrayBlockingQueue
RejectedExecutionHandler
ThreadPoolExecutor.AbortPolicy()
RejectedExecutionException
CallerRunsPolicy
DiscardPolicy
通过这种方式,我们不仅创建了一个固定大小的线程池,更重要的是,我们为它加上了一道“安全阀”,避免了因任务量过大导致内存溢出的风险。这种对细节的掌控,在我看来,是构建健壮并发应用的关键。
以上就是如何在Java中创建固定大小线程池的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号