一、线程池概述
1.1 什么是线程池
线程池(Thread Pool)是一种多线程处理形式,它预先创建一组线程并管理它们的生命周期,当有任务到来时,从池中分配一个空闲线程来执行任务,任务完成后线程返回池中等待下一次分配,而不是直接销毁。这种机制避免了频繁创建和销毁线程的开销,提高了系统性能。
1.2 为什么需要线程池
在传统模式下,每个任务到来时都创建一个新线程会有以下问题:
- 线程创建和销毁开销大(涉及操作系统层面的资源分配)
- 线程数量无限制增长会导致系统资源耗尽
- 缺乏统一管理,难以监控和调优
线程池通过以下方式解决这些问题:
- 资源复用:减少线程创建和销毁的开销
- 流量控制:通过限制线程数量防止系统过载
- 统一管理:提供任务队列、拒绝策略等机制
二、JDK1.8线程池核心实现
2.1 线程池核心类结构
JDK1.8中线程池的核心实现位于java.util.concurrent包,主要类包括:
- Executor:最基础的执行接口
- ExecutorService:扩展了Executor,增加了生命周期管理
- ThreadPoolExecutor:线程池的核心实现类
- Executors:线程池的工厂类,提供常用配置
// 类继承关系
Executor
↑
ExecutorService
↑
AbstractExecutorService
↑
ThreadPoolExecutor
2.2 ThreadPoolExecutor核心参数
ThreadPoolExecutor的构造函数包含7个核心参数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize:核心线程数,即使空闲也不会被回收
- maximumPoolSize:最大线程数,包括核心线程和非核心线程
- keepAliveTime:非核心线程空闲存活时间
- unit:存活时间单位
- workQueue:任务队列,保存待执行任务
- threadFactory:线程工厂,用于创建线程
- handler:拒绝策略,当队列和线程池都满时的处理方式
2.3 线程池状态机
ThreadPoolExecutor使用一个AtomicInteger同时记录线程池状态和工作线程数:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 高3位表示状态,低29位表示工作线程数
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 线程池状态
private static final int RUNNING = -1 << COUNT_BITS; // 111
private static final int SHUTDOWN = 0 << COUNT_BITS; // 000
private static final int STOP = 1 << COUNT_BITS; // 001
private static final int TIDYING = 2 << COUNT_BITS; // 010
private static final int TERMINATED = 3 << COUNT_BITS; // 011
状态转换:
- RUNNING -> SHUTDOWN:调用shutdown()
- (RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()
- SHUTDOWN -> TIDYING:队列和池都为空
- STOP -> TIDYING:池为空
- TIDYING -> TERMINATED:terminated()钩子执行完毕
三、线程池工作流程
3.1 任务提交流程
- 当前线程数 < corePoolSize:创建新线程执行任务
- 当前线程数 ≥ corePoolSize:尝试将任务加入队列
- 队列已满且线程数 < maximumPoolSize:创建新线程执行任务
- 队列已满且线程数 ≥ maximumPoolSize:执行拒绝策略
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 1. 当前线程数 < corePoolSize
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2. 尝试加入队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 3. 队列已满,尝试创建非核心线程
else if (!addWorker(command, false))
// 4. 执行拒绝策略
reject(command);
}
3.2 Worker内部类
ThreadPoolExecutor使用Worker类包装工作线程:
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
final Thread thread; // 实际执行线程
Runnable firstTask; // 初始任务
Worker(Runnable firstTask) {
setState(-1); // 禁止中断直到runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
// 省略锁实现...
}
3.3 runWorker方法
这是线程执行任务的核心方法:
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 允许中断
boolean completedAbruptly = true;
try {
// 循环获取任务
while (task != null || (task = getTask()) != null) {
w.lock();
// 检查线程状态
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) {
wt.interrupt();
}
try {
beforeExecute(wt, task); // 钩子方法
try {
task.run(); // 执行任务
afterExecute(task, null); // 钩子方法
} catch (Throwable ex) {
afterExecute(task, ex); // 钩子方法
throw ex;
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
四、线程池关键组件
4.1 任务队列(WorkQueue)
JDK提供了多种BlockingQueue实现:
- ArrayBlockingQueue:有界数组队列
- LinkedBlockingQueue:无界链表队列(默认最大Integer.MAX_VALUE)
- SynchronousQueue:不存储元素的队列,每个插入必须等待一个移除
- PriorityBlockingQueue:带优先级的无界队列
4.2 拒绝策略(RejectedExecutionHandler)
JDK提供了4种内置拒绝策略:
- AbortPolicy(默认):抛出RejectedExecutionException
- CallerRunsPolicy:由提交任务的线程直接执行
- DiscardPolicy:静默丢弃任务
- DiscardOldestPolicy:丢弃队列中最老的任务,然后重试
自定义拒绝策略示例:
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
(r, executor) -> {
// 自定义拒绝策略:记录日志后重试
System.out.println("Task " + r + " rejected, retrying...");
try {
executor.getQueue().put(r); // 阻塞直到队列有空闲
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
4.3 线程工厂(ThreadFactory)
自定义线程工厂示例:
public class CustomThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public CustomThreadFactory(String poolName) {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = poolName + "-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
五、Executors工厂类
JDK提供了几种常用线程池配置:
- newFixedThreadPool:固定大小线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- newCachedThreadPool:可缓存线程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- newSingleThreadExecutor:单线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- newScheduledThreadPool:定时任务线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
六、线程池监控与调优
6.1 监控指标
可以通过ThreadPoolExecutor提供的方法获取运行状态:
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
// 获取核心线程数
int corePoolSize = executor.getCorePoolSize();
// 获取当前线程数
int poolSize = executor.getPoolSize();
// 获取活跃线程数
int activeCount = executor.getActiveCount();
// 获取已完成任务数
long completedTaskCount = executor.getCompletedTaskCount();
// 获取任务总数
long taskCount = executor.getTaskCount();
// 获取队列中的任务数
int queueSize = executor.getQueue().size();
6.2 动态调整参数
ThreadPoolExecutor支持运行时调整参数:
executor.setCorePoolSize(5); // 调整核心线程数
executor.setMaximumPoolSize(10); // 调整最大线程数
executor.setKeepAliveTime(30, TimeUnit.SECONDS); // 调整空闲时间
6.3 调优建议
- CPU密集型任务:建议线程数 = CPU核心数 + 1
- IO密集型任务:建议线程数 = CPU核心数 * (1 + 平均等待时间/平均计算时间)
- 混合型任务:考虑将任务分为CPU密集和IO密集两部分,分别使用不同线程池
七、线程池使用示例
7.1 基础使用
public class ThreadPoolDemo {
public static void main(String[] args) {
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 4, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
// 提交任务
for (int i = 0; i < 6; i++) {
final int taskId = i;
executor.execute(() -> {
System.out.println("Task " + taskId + " is running on "
+ Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 关闭线程池
executor.shutdown();
try {
executor.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All tasks completed");
}
}
7.2 定时任务示例
public class ScheduledThreadPoolDemo {
public static void main(String[] args) {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// 延迟执行
scheduler.schedule(() -> {
System.out.println("Delayed task executed after 3 seconds");
}, 3, TimeUnit.SECONDS);
// 固定频率执行
scheduler.scheduleAtFixedRate(() -> {
System.out.println("Fixed rate task executed every 2 seconds");
}, 1, 2, TimeUnit.SECONDS);
// 固定延迟执行
scheduler.scheduleWithFixedDelay(() -> {
try {
Thread.sleep(1000);
System.out.println("Fixed delay task executed with 2 seconds delay after completion");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 1, 2, TimeUnit.SECONDS);
// 10秒后关闭
scheduler.schedule(() -> {
scheduler.shutdown();
System.out.println("Scheduler shutdown");
}, 10, TimeUnit.SECONDS);
}
}
八、线程池常见问题
8.1 线程池大小设置
- 设置过小:无法充分利用CPU资源,任务等待时间长
- 设置过大:线程上下文切换开销大,内存消耗增加
8.2 任务队列选择
- 无界队列:可能导致内存溢出
- 有界队列:需要合理设置队列大小
8.3 资源泄漏
确保任务不会无限期阻塞,否则会导致线程无法回收:
executor.execute(() -> {
try {
while (true) { // 错误示例:无限循环
// 处理任务
}
} catch (Exception e) {
e.printStackTrace();
}
});
8.4 死锁风险
避免在任务中提交新的任务到同一个线程池并等待其完成:
executor.execute(() -> {
Future<?> future = executor.submit(() -> {
// 子任务
});
future.get(); // 可能导致死锁
});
九、总结
线程池是Java并发编程中的重要组件,JDK1.8中的ThreadPoolExecutor提供了灵活而强大的线程池实现。理解其内部工作原理、状态机设计、任务调度流程等底层机制,有助于我们更好地使用和调优线程池。在实际应用中,应根据具体场景选择合适的线程池配置,并注意监控线程池的运行状态,避免常见问题。
700

被折叠的 条评论
为什么被折叠?



