在现代高并发的互联网应用中,多线程编程是提升系统性能的重要手段之一。然而,线程的创建、销毁以及管理成本较高,直接使用线程可能会导致系统资源耗尽。为了解决这一问题,Java 提供了线程池(ThreadPoolExecutor
)机制,能够高效地管理线程的生命周期,提升系统性能。本文将深入探讨线程的生命周期、线程池的工作原理,并结合底层源码分析其实现细节。
在 Java 中,线程的生命周期可以分为以下几个状态:
NEW(新建状态)
线程被创建但尚未启动,此时线程对象已经初始化,但还未调用 start()
方法。
RUNNABLE(可运行状态)
线程调用了 start()
方法后进入 RUNNABLE 状态。此时线程可能正在运行,也可能在等待 CPU 时间片。
BLOCKED(阻塞状态)
线程因为等待锁(如 synchronized
关键字)而进入阻塞状态,直到获取到锁后才能回到 RUNNABLE 状态。
WAITING(等待状态)
线程调用了 wait()
、join()
或 LockSupport.park()
等方法,进入无限期等待状态,直到其他线程显式唤醒。
TIMED_WAITING(超时等待状态)
线程调用了 sleep()
、wait(timeout)
或 join(timeout)
等方法,进入有限期的等待状态。
TERMINATED(终止状态)
线程执行完毕或因异常退出,进入终止状态。
线程的状态转换可以通过以下代码验证:
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000); // TIMED_WAITING
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(thread.getState()); // NEW
thread.start();
System.out.println(thread.getState()); // RUNNABLE
Thread.sleep(500);
System.out.println(thread.getState()); // TIMED_WAITING
thread.join();
System.out.println(thread.getState()); // TERMINATED
线程池的核心思想是复用线程,避免频繁创建和销毁线程带来的性能开销。Java 中的 ThreadPoolExecutor
是线程池的核心实现类,其工作原理可以分为以下几个部分:
ThreadPoolExecutor
的构造函数包含以下核心参数:
当一个任务提交到线程池时,ThreadPoolExecutor
的执行流程如下:
corePoolSize
,则创建新线程执行任务。corePoolSize
,则将任务放入任务队列(workQueue
)。maximumPoolSize
,则创建新线程执行任务。maximumPoolSize
且任务队列已满,则根据拒绝策略处理任务。以下是 ThreadPoolExecutor
的核心方法 execute()
的源码解析:
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. 如果线程数已达到 maximumPoolSize,执行拒绝策略
reject(command);
}
ThreadPoolExecutor
使用 ctl
的高 3 位表示线程池的状态:
ThreadPoolExecutor
使用 Worker
类封装线程和任务。Worker
实现了 Runnable
接口,其 run()
方法会不断从任务队列中获取任务并执行。
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
final Thread thread;
Runnable firstTask;
Worker(Runnable firstTask) {
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
// ...
}
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.isInterrupted())
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);
}
}
通过对线程生命周期和 ThreadPoolExecutor
的深入分析,我们可以更好地理解多线程编程的核心机制。线程池通过复用线程、管理任务队列和动态调整线程数,显著提升了系统的性能和稳定性。在实际开发中,合理配置线程池参数(如核心线程数、任务队列类型和拒绝策略)是优化高并发系统的关键。
对于北京互联网大厂的高并发场景,深入掌握线程池的底层实现和调优技巧,能够帮助开发者在性能与资源之间找到最佳平衡点,为系统的稳定运行提供有力保障。
希望本文能为大家提供有价值的参考,欢迎在评论区交流讨论!