Java 线程池状态的整理阶段
Java 线程池状态概述
在深入探讨 Java 线程池状态的整理阶段之前,我们先来了解一下线程池状态的整体概念。Java 线程池的状态是用来标识线程池当前所处的运行情况,它对于线程池的控制和管理至关重要。线程池的状态主要通过一个原子整数变量 ctl
来表示,这个变量不仅包含了线程池的状态信息,还记录了线程池中当前活动线程的数量。
在 java.util.concurrent.ThreadPoolExecutor
类中,ctl
是这样定义的:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
这里 ctlOf
方法用于将线程池状态和线程数合并成 ctl
的值,而 RUNNING
是线程池的初始状态。
线程池的基本状态
- RUNNING
- 含义:这是线程池的初始状态。在线程池处于
RUNNING
状态时,它可以接受新的任务并且处理阻塞队列中的任务。 - 状态值:
RUNNING
的状态值是一个负数,具体来说,它的值为(1 << COUNT_BITS) * -1
,其中COUNT_BITS
是用来表示线程数的位数,在 32 位系统中,COUNT_BITS
通常为 29,这意味着RUNNING
的状态值约为-1 << 29
。 - 代码示例:
- 含义:这是线程池的初始状态。在线程池处于
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolRunningStateExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
// 此时线程池处于RUNNING状态
System.out.println("线程池状态: " + ((ThreadPoolExecutor) executorService).getPoolState());
executorService.submit(() -> {
System.out.println("执行任务");
});
executorService.shutdown();
}
}
在这个示例中,通过 Executors.newFixedThreadPool(5)
创建的线程池初始状态就是 RUNNING
,我们可以通过 ((ThreadPoolExecutor) executorService).getPoolState()
方法获取其状态(实际 ThreadPoolExecutor
并没有直接提供 getPoolState
方法,这里为了示例示意,假设存在这样的方法,实际可通过 ctl
相关逻辑判断)。
- SHUTDOWN
- 含义:当调用线程池的
shutdown()
方法后,线程池会进入SHUTDOWN
状态。在这个状态下,线程池不再接受新的任务,但会继续处理阻塞队列中已有的任务。 - 状态值:
SHUTDOWN
的状态值为0
。 - 代码示例:
- 含义:当调用线程池的
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadPoolShutdownStateExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
executorService.submit(() -> {
System.out.println("执行任务1");
});
executorService.submit(() -> {
System.out.println("执行任务2");
});
executorService.shutdown();
try {
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
executorService.shutdownNow();
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("Pool did not terminate");
}
}
} catch (InterruptedException ie) {
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
// 此时线程池处于SHUTDOWN状态,不再接受新任务,但会处理队列中的任务
}
}
在这个示例中,调用 shutdown()
方法后,线程池进入 SHUTDOWN
状态,此时提交新任务会被拒绝,但已在队列中的任务会继续执行。
- STOP
- 含义:当调用线程池的
shutdownNow()
方法后,线程池会进入STOP
状态。在这个状态下,线程池不仅不再接受新任务,而且会尝试停止当前正在执行的任务,并清空阻塞队列。 - 状态值:
STOP
的状态值为(1 << COUNT_BITS)
。 - 代码示例:
- 含义:当调用线程池的
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolStopStateExample {
public static void main(String[] args) {
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
ThreadPoolExecutor executorService = new ThreadPoolExecutor(
2, 5, 10, TimeUnit.SECONDS, taskQueue);
executorService.submit(() -> {
try {
TimeUnit.SECONDS.sleep(5);
System.out.println("任务执行中");
} catch (InterruptedException e) {
System.out.println("任务被中断");
}
});
executorService.shutdownNow();
// 此时线程池处于STOP状态,尝试停止正在执行的任务并清空队列
}
}
在这个示例中,调用 shutdownNow()
方法后,线程池进入 STOP
状态,正在执行的任务可能会被中断,队列中的任务会被清空。
- TIDYING
- 含义:当所有任务都已终止(包括正在执行的任务被停止以及队列中的任务被处理完),并且线程池中的活动线程数为 0 时,线程池会进入
TIDYING
状态。在这个状态下,会执行terminated()
方法,这个方法可以被重写来进行一些自定义的收尾工作。 - 状态值:
TIDYING
的状态值为(1 << COUNT_BITS) + 1
。 - 代码示例:
- 含义:当所有任务都已终止(包括正在执行的任务被停止以及队列中的任务被处理完),并且线程池中的活动线程数为 0 时,线程池会进入
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolTidyingStateExample {
public static void main(String[] args) {
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
ThreadPoolExecutor executorService = new ThreadPoolExecutor(
2, 5, 10, TimeUnit.SECONDS, taskQueue) {
@Override
protected void terminated() {
System.out.println("线程池进入TIDYING状态,执行自定义收尾工作");
}
};
executorService.submit(() -> {
System.out.println("执行任务");
});
executorService.shutdown();
try {
if (executorService.awaitTermination(60, TimeUnit.SECONDS)) {
// 所有任务执行完毕,线程池进入TIDYING状态
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在这个示例中,当所有任务执行完毕且活动线程数为 0 时,线程池进入 TIDYING
状态,并执行重写的 terminated()
方法。
- TERMINATED
- 含义:当
terminated()
方法执行完毕后,线程池会进入TERMINATED
状态。这表示线程池已经彻底终止,不再有任何活动。 - 状态值:
TERMINATED
的状态值为(1 << COUNT_BITS) + 2
。 - 代码示例:
- 含义:当
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolTerminatedStateExample {
public static void main(String[] args) {
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
ThreadPoolExecutor executorService = new ThreadPoolExecutor(
2, 5, 10, TimeUnit.SECONDS, taskQueue) {
@Override
protected void terminated() {
System.out.println("线程池进入TIDYING状态,执行自定义收尾工作");
}
};
executorService.submit(() -> {
System.out.println("执行任务");
});
executorService.shutdown();
try {
if (executorService.awaitTermination(60, TimeUnit.SECONDS)) {
// 所有任务执行完毕,线程池进入TIDYING状态
// 当terminated()方法执行完毕后,进入TERMINATED状态
System.out.println("线程池已进入TERMINATED状态");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在这个示例中,当 terminated()
方法执行完毕,线程池进入 TERMINATED
状态。
线程池状态转换的内部机制
- 从 RUNNING 到 SHUTDOWN
- 触发条件:当调用线程池的
shutdown()
方法时,线程池从RUNNING
状态转换为SHUTDOWN
状态。 - 内部实现:在
ThreadPoolExecutor
的shutdown()
方法中,首先会通过compareAndSetState(RUNNING, SHUTDOWN)
方法尝试将线程池状态从RUNNING
转换为SHUTDOWN
。如果转换成功,会遍历工作线程并中断空闲线程(通过interruptIdleWorkers()
方法),这样可以使得空闲线程尽快结束等待,去处理队列中的任务。
- 触发条件:当调用线程池的
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // 用于子类扩展
} finally {
mainLock.unlock();
}
tryTerminate();
}
这里 advanceRunState(SHUTDOWN)
方法内部使用 compareAndSetState
来更新状态,interruptIdleWorkers()
方法如下:
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
- 从 RUNNING 或 SHUTDOWN 到 STOP
- 触发条件:当调用线程池的
shutdownNow()
方法时,无论线程池当前是RUNNING
还是SHUTDOWN
状态,都会转换为STOP
状态。 - 内部实现:在
shutdownNow()
方法中,首先通过compareAndSetState
尝试将状态转换为STOP
。然后,会遍历工作线程并中断所有线程(通过interruptWorkers()
方法),同时清空任务队列。
- 触发条件:当调用线程池的
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
这里 interruptWorkers()
方法会中断所有工作线程:
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
而 drainQueue()
方法用于清空任务队列:
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
ArrayList<Runnable> taskList = new ArrayList<>();
q.drainTo(taskList);
if (!q.isEmpty()) {
for (Runnable r : q.toArray(new Runnable[0])) {
if (q.remove(r))
taskList.add(r);
}
}
return taskList;
}
- 从 SHUTDOWN 或 STOP 到 TIDYING
- 触发条件:当所有任务都已终止(正在执行的任务被停止以及队列中的任务被处理完),并且线程池中的活动线程数为 0 时,线程池从
SHUTDOWN
或STOP
状态转换为TIDYING
状态。 - 内部实现:在
tryTerminate()
方法中,会进行状态转换的判断。如果当前状态是SHUTDOWN
且任务队列已空,或者当前状态是STOP
且活动线程数为 0,就会尝试将状态转换为TIDYING
,并调用terminated()
方法。
- 触发条件:当所有任务都已终止(正在执行的任务被停止以及队列中的任务被处理完),并且线程池中的活动线程数为 0 时,线程池从
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN &&!workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
- 从 TIDYING 到 TERMINATED
- 触发条件:当
terminated()
方法执行完毕后,线程池从TIDYING
状态转换为TERMINATED
状态。 - 内部实现:在
tryTerminate()
方法中,当成功将状态转换为TIDYING
并调用terminated()
方法后,会通过ctl.set(ctlOf(TERMINATED, 0))
将状态设置为TERMINATED
,并通过termination.signalAll()
唤醒所有等待在termination
条件变量上的线程。
- 触发条件:当
理解线程池状态在实际应用中的重要性
- 资源管理
- 了解线程池的状态有助于进行有效的资源管理。例如,当线程池处于
SHUTDOWN
状态时,我们知道它不再接受新任务,但仍在处理队列中的任务,这时候可以合理地等待其处理完毕,避免资源的浪费。如果在不合适的时候强制停止线程池(如在任务关键阶段进入STOP
状态),可能会导致数据不一致或任务不完整。 - 代码示例:
- 了解线程池的状态有助于进行有效的资源管理。例如,当线程池处于
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolResourceManagementExample {
public static void main(String[] args) {
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
ThreadPoolExecutor executorService = new ThreadPoolExecutor(
2, 5, 10, TimeUnit.SECONDS, taskQueue);
executorService.submit(() -> {
// 模拟一个长时间运行的任务
try {
TimeUnit.SECONDS.sleep(5);
System.out.println("长时间运行任务执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 先进入SHUTDOWN状态,等待任务自然结束
executorService.shutdown();
try {
if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
// 如果10秒内未完成,再考虑进入STOP状态
executorService.shutdownNow();
if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
System.err.println("Pool did not terminate");
}
}
} catch (InterruptedException e) {
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
在这个示例中,先进入 SHUTDOWN
状态等待任务完成,如果超时未完成再进入 STOP
状态,这样可以更好地管理资源,避免过早终止任务。
- 错误处理和监控
- 线程池状态可以作为监控和错误处理的重要依据。例如,如果线程池意外进入
STOP
状态,可能意味着有异常情况发生,需要进行排查。通过监控线程池状态的变化,可以及时发现并处理潜在的问题,保证系统的稳定性。 - 代码示例:
- 线程池状态可以作为监控和错误处理的重要依据。例如,如果线程池意外进入
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolErrorHandlingExample {
private static ThreadPoolExecutor executorService;
static {
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
executorService = new ThreadPoolExecutor(
2, 5, 10, TimeUnit.SECONDS, taskQueue);
}
public static void monitorThreadPool() {
new Thread(() -> {
while (true) {
int state = executorService.getPoolState(); // 假设存在获取状态的方法
if (state == ThreadPoolExecutor.STOP) {
System.err.println("线程池意外进入STOP状态,进行排查");
// 这里可以添加具体的排查逻辑,如记录日志、通知运维等
}
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
public static void main(String[] args) {
monitorThreadPool();
executorService.submit(() -> {
// 模拟任务执行
System.out.println("执行任务");
});
// 模拟一些可能导致异常进入STOP状态的操作(这里省略实际异常触发逻辑)
}
}
在这个示例中,通过一个线程定时监控线程池状态,如果发现进入 STOP
状态,就进行相应的错误处理。
- 性能优化
- 理解线程池状态有助于进行性能优化。例如,在系统负载较低时,可以通过合理调整线程池状态(如从
RUNNING
进入SHUTDOWN
再到TERMINATED
)来释放资源,降低系统开销。而在负载增加时,确保线程池处于RUNNING
状态并能高效处理任务。 - 代码示例:
- 理解线程池状态有助于进行性能优化。例如,在系统负载较低时,可以通过合理调整线程池状态(如从
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolPerformanceOptimizationExample {
private static ThreadPoolExecutor executorService;
static {
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
executorService = new ThreadPoolExecutor(
2, 5, 10, TimeUnit.SECONDS, taskQueue);
}
public static void adjustThreadPoolAccordingToLoad() {
// 模拟根据负载调整线程池状态
boolean isLowLoad = true; // 假设通过某种方式判断当前负载低
if (isLowLoad) {
executorService.shutdown();
try {
if (executorService.awaitTermination(10, TimeUnit.SECONDS)) {
System.out.println("线程池已优雅关闭,释放资源");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
// 如果负载高,确保线程池处于RUNNING状态并调整参数等
}
}
public static void main(String[] args) {
adjustThreadPoolAccordingToLoad();
}
}
在这个示例中,根据负载情况调整线程池状态,实现性能优化。
总结线程池状态相关要点
- 状态转换的条件和顺序
- 线程池状态从
RUNNING
开始,可能通过shutdown()
方法转换为SHUTDOWN
,或通过shutdownNow()
方法转换为STOP
。从SHUTDOWN
或STOP
状态,在满足任务终止且活动线程数为 0 的条件下转换为TIDYING
,最后在terminated()
方法执行完毕后转换为TERMINATED
。 - 理解这个顺序对于正确控制线程池至关重要,错误的状态转换操作可能导致任务丢失、资源未释放等问题。
- 线程池状态从
- 对任务执行的影响
- 不同的线程池状态对任务执行有不同的影响。
RUNNING
状态下正常接受和处理任务,SHUTDOWN
状态不再接受新任务但处理队列任务,STOP
状态停止正在执行任务并清空队列。TIDYING
和TERMINATED
状态表示任务执行的收尾和终结阶段。 - 在编写多线程应用程序时,必须考虑线程池状态对任务执行的影响,以确保任务的完整性和正确性。
- 不同的线程池状态对任务执行有不同的影响。
- 应用场景和最佳实践
- 在实际应用中,根据不同的业务需求选择合适的线程池状态转换时机。例如,在系统关闭时,优先选择
shutdown()
方法进行优雅关闭,给任务足够的时间完成。在紧急情况下,如系统出现严重错误,可能需要使用shutdownNow()
方法强制停止线程池。 - 同时,结合监控和错误处理机制,及时发现和处理线程池状态异常,保证系统的稳定性和可靠性。
- 在实际应用中,根据不同的业务需求选择合适的线程池状态转换时机。例如,在系统关闭时,优先选择
通过深入理解 Java 线程池状态及其转换机制,开发者能够更好地控制和管理线程池,提高多线程应用程序的性能和稳定性。在实际开发中,根据具体业务场景合理运用线程池状态相关知识,是实现高效并发编程的关键之一。