上一篇
Java中可通过线程池(如ThreadPoolExecutor)设置核心/最大线程数限制并发量,或使用信号量(Semaphore)控制同时执行的线程数量。
为什么需要限制线程数?
- 资源消耗:每个线程占用内存(默认1MB栈空间)和CPU时间片,线程过多导致内存溢出(
OutOfMemoryError)。 - 性能下降:频繁线程切换增加系统开销,降低吞吐量。
- 稳定性风险:未受控的线程可能引发死锁或系统崩溃。
核心方法:使用线程池(推荐)
Java通过java.util.concurrent包提供线程池管理,这是最标准的解决方案。
固定大小线程池(FixedThreadPool)
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadLimitExample {
public static void main(String[] args) {
// 限制最大线程数为5
int maxThreads = 5;
ExecutorService executor = Executors.newFixedThreadPool(maxThreads);
// 提交10个任务(实际只有5个线程并发执行)
for (int i = 0; i < 10; i++) {
executor.execute(() -> {
System.out.println("Task running by " + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟任务耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown(); // 关闭线程池
}
}
- 原理:线程池维护固定数量的工作线程,多余任务进入队列等待。
- 优点:自动管理线程生命周期,避免资源泄漏。
自定义线程池(ThreadPoolExecutor)
更灵活地控制队列和拒绝策略:
import java.util.concurrent.*;
public class CustomThreadPool {
public static void main(String[] args) {
int coreThreads = 2; // 核心线程数
int maxThreads = 4; // 最大线程数
int queueCapacity = 10; // 任务队列容量
ExecutorService executor = new ThreadPoolExecutor(
coreThreads,
maxThreads,
60, TimeUnit.SECONDS, // 空闲线程超时时间
new ArrayBlockingQueue<>(queueCapacity),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() // 队列满时由提交线程执行任务
);
// 提交20个任务(最大并发4线程 + 队列存储10个 + 剩余由主线程执行)
for (int i = 0; i < 20; i++) {
executor.execute(/* 任务逻辑 */);
}
executor.shutdown();
}
}
- 关键参数:
corePoolSize:常驻核心线程数。maximumPoolSize:线程池最大容量。workQueue:任务队列(推荐有界队列如ArrayBlockingQueue)。RejectedExecutionHandler:拒绝策略(如CallerRunsPolicy防止任务丢失)。
其他辅助方法
信号量(Semaphore)
限制同时访问资源的线程数:

import java.util.concurrent.Semaphore;
public class SemaphoreExample {
private static final int MAX_CONCURRENT_THREADS = 3;
private static final Semaphore semaphore = new Semaphore(MAX_CONCURRENT_THREADS);
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
semaphore.acquire(); // 获取许可
System.out.println("Thread started: " + Thread.currentThread().getName());
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // 释放许可
}
}).start();
}
}
}
- 适用场景:控制特定资源的并发访问(如数据库连接)。
计数器(CountDownLatch/CyclicBarrier)
通过同步工具间接控制线程并发:
// 使用CountDownLatch等待线程完成
ExecutorService executor = Executors.newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(MAX_THREADS);
for (Task task : tasks) {
executor.execute(() -> {
try {
task.execute();
} finally {
latch.countDown(); // 任务完成计数减1
}
});
if (latch.getCount() == 0) break; // 达到上限停止提交
}
latch.await(); // 等待所有任务完成
- 注意:需手动控制任务提交逻辑。
最佳实践与注意事项
-
线程数设置公式:

- CPU密集型:
线程数 = CPU核心数 + 1 - I/O密集型:
线程数 = CPU核心数 * (1 + 平均等待时间/平均计算时间) - 可通过
Runtime.getRuntime().availableProcessors()获取CPU核心数。
- CPU密集型:
-
避免无界队列:
- 使用
LinkedBlockingQueue需指定容量,否则任务堆积导致内存溢出。
- 使用
-
防止线程泄漏:

- 确保调用
shutdown()或shutdownNow()关闭线程池。
- 确保调用
-
监控线程状态:
- 利用
ThreadMXBean或APM工具(如Arthas)监控线程数量。
- 利用
- 首选线程池:
FixedThreadPool或ThreadPoolExecutor是工业级解决方案。 - 场景适配:高并发用线程池,资源控制用信号量。
- 参数调优:根据任务类型(CPU/I/O密集型)动态调整线程数。
引用说明:
- Oracle官方文档:ThreadPoolExecutor
- Java并发编程实践(Brian Goetz)
- 阿里开发手册:线程池资源隔离规范
