Java线程池关闭、等待、线程结束与JVM退出

1. 说明

JDK 中的 java.util.concurrent.ThreadPoolExecutor 类是常用的线程池,以上线程池的关闭、等待与线程结束等处理在使用时存在一些反直觉的情况,需要注意

2. 总结

使用 ThreadPoolExecutor.shutdown()、shutdownNow() 方法关闭线程池后的一些操作的执行结果总结如下:

线程池关闭后的操作执行结果 / 线程池关闭方式ThreadPoolExecutor.shutdown()ThreadPoolExecutor.shutdownNow()
对正在执行任务的影响没有影响线程会被中断
是否丢弃队列中的任务不丢弃丢弃
是否等待存量任务执行完毕不等待不等待
使用 awaitTermination() 方法等待结束的任务范围正在执行的任务 + 队列中的任务正在执行的任务
继续执行任务使用拒绝策略处理任务使用拒绝策略处理任务
活跃线程结束方式任务执行完毕后结束1. 线程被中断后结束
2. 任务执行完毕后结束
空闲线程结束方式线程被中断后结束线程被中断后结束
线程执行出现异常结束后是否会创建新线程会创建不创建

在应用停止阶段需要关闭线程池,不能使用 ThreadPoolExecutor.shutdownNow() 方法,因为会导致任务队列中的任务会被丢弃不再执行;且当前执行任务的线程被中断,使用 Druid 数据源的数据库操作会失败;在关闭线程池时需要使用 shutdown() 方法

调用 ThreadPoolExecutor.shutdown()、shutdownNow() 方法后,不会等待线程池中的任务执行完毕,需要调用 awaitTermination() 等待活跃线程结束

3. 线程池关闭操作对正在执行任务的影响

3.1. ThreadPoolExecutor.shutdown() 对正在执行任务的影响

执行 ThreadPoolExecutor.shutdown() 方法后,正在执行任务的线程不会受到影响,即正在执行的任务会继续执行

3.2. ThreadPoolExecutor.shutdownNow() 对正在执行任务的影响

执行 ThreadPoolExecutor.shutdownNow() 方法后,正在执行任务的线程会被中断,通过调用 java.lang.Thread#interrupt() 方法触发

假如当前线程正在调用 Object 类的 wait(), wait(long), wait(long, int) 方法,或 Thread 类的 join(), join(long), join(long, int), sleep(long), sleep(long, int),会出现异常 InterruptedException

假如正在执行的任务代码中有调用以上方法且被中断,后续的影响取决于是否有进行异常捕获,及异常捕获中的处理

3.2.1. 对使用 Druid 数据源的数据库操作影响

假如通过 ThreadPoolExecutor 执行线程池操作的任务中,使用 Druid 数据源进行数据库操作,则执行 ThreadPoolExecutor.shutdownNow() 方法后数据库操作会失败,会出现异常 “Caused by: java.sql.SQLException: interrupt”

在使用 Druid 数据源时,进行数据库操作之前需要先获取数据库连接,需要调用 com.alibaba.druid.pool.DruidDataSource#getConnectionInternal() 方法,该方法中调用了 lock 字段的 lockInterruptibly(),该字段类型为 java.util.concurrent.locks.ReentrantLock,代码如下所示:

protected ReentrantLock lock;

            try {
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                connectErrorCountUpdater.incrementAndGet(this);
                throw new SQLException("interrupt", e);
            }

在 java.util.concurrent.locks.ReentrantLock#lockInterruptibly() 方法中,调用了 sync 字段的 acquireInterruptibly() 方法

sync.acquireInterruptibly(1);

以上方法对应 java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireInterruptibly(),在其中会判断若当前线程已被中断,则抛出 InterruptedException 异常

        if (Thread.interrupted())
            throw new InterruptedException();

因此 Druid 获取数据库连接时调用的 DruidDataSource#getConnectionInternal() 方法会抛出 SQLException 异常,无法获取到数据库连接,数据库操作会失败

3.3. 代码对比

对比 java.util.concurrent.ThreadPoolExecutor 类的 shutdown() 与 shutdownNow() 方法

  • shutdown() 方法

代码注释中说明,(调用当前方法后)之前提交的任务会执行:

previously submitted tasks are executed

调用了 interruptIdleWorkers() 方法,用于中断空闲的 worker 线程

没有中断活跃的 worker 线程

  • shutdownNow() 方法

代码注释中说明,会尝试停止所有正在执行的任务,并中断正在等待的任务:

Attempts to stop all actively executing tasks, halts the processing of waiting tasks

调用了 interruptWorkers() 方法,用于中断所有的 worker 线程

4. 线程池关闭操作是否会丢弃队列中的任务

4.1. ThreadPoolExecutor.shutdown() 是否会丢弃队列中的任务

执行 ThreadPoolExecutor.shutdown() 方法后,不会丢弃队列中的任务,即队列中的任务会继续执行

4.2. ThreadPoolExecutor.shutdownNow() 是否会丢弃队列中的任务

执行 ThreadPoolExecutor.shutdownNow() 方法后,会丢弃队列中的任务,即队列中的任务不会继续执行

4.3. 代码对比

  • shutdownNow() 方法

代码注释中说明,会将任务队列中的任务删除:

These tasks are drained (removed) from the task queue upon return from this method.

会调用 drainQueue() 方法,会将任务队列清空

  • shutdown() 方法

不会调用 drainQueue() 方法,没有清空任务队列

5. 线程池关闭操作是否会等待存量任务执行完毕

5.1. ThreadPoolExecutor.shutdown() 是否会等待存量任务执行完毕

shutdown() 方法不会等待存量任务执行完毕

5.2. ThreadPoolExecutor.shutdownNow() 是否会等待存量任务执行完毕

shutdownNow() 方法不会等待存量任务执行完毕

5.3. 代码对比

  • shutdown() 方法

代码注释中说明,不会等待之前提交的任务执行完毕,需要使用 awaitTermination() 方法进行等待:

This method does not wait for previously submitted tasks to complete execution. Use awaitTermination to do that.
  • shutdownNow() 方法

代码注释中说明,不会等待正在执行的任务执行完毕,需要使用 awaitTermination() 方法进行等待:

This method does not wait for actively executing tasks to terminate. Use awaitTermination to do that.

6. 等待线程池任务执行完毕

6.1. shutdown()、shutdownNow() 与 awaitTermination()

在执行 ThreadPoolExecutor.shutdown()、shutdownNow() 方法后,假如需要等待任务执行完毕,需要使用 awaitTermination() 方法

调用 ThreadPoolExecutor.shutdown() 后,再调用 awaitTermination() 方法,等待任务结束的范围是:正在执行的任务 + 队列中的任务(正在执行的任务 + 队列中的任务都会执行)

调用 ThreadPoolExecutor.shutdownNow() 后,再调用 awaitTermination() 方法,等待任务结束的范围是:正在执行的任务(只有正在执行的任务还会执行,但会被中断)

6.2. ThreadPoolExecutor.awaitTermination() 需要等待的时间

ThreadPoolExecutor.awaitTermination() 方法实现了 java.util.concurrent.ExecutorService 接口的对应方法,代码注释中说明,当前方法会等待直到满足以下某条件满足:

执行 shutdown 操作后所有的任务执行完毕
超过指定时间
当前线程被中断
Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.

6.3. ThreadPoolExecutor.awaitTermination() 等待任务执行完毕的实现方式

6.3.1. ThreadPoolExecutor 的状态

ThreadPoolExecutor 线程池有五种状态

代码注释中有以下说明:

RUNNING:  Accept new tasks and process queued tasks
SHUTDOWN: Don't accept new tasks, but process queued tasks
STOP:     Don't accept new tasks, don't process queued tasks, and interrupt in-progress tasks
TIDYING:  All tasks have terminated, workerCount is zero, the thread transitioning to state TIDYING will run the terminated() hook method
TERMINATED: terminated() has completed

状态流转条件如下:

RUNNING -> SHUTDOWN
   On invocation of shutdown(), perhaps implicitly in finalize()
(RUNNING or SHUTDOWN) -> STOP
   On invocation of shutdownNow()
SHUTDOWN -> TIDYING
   When both queue and pool are empty
STOP -> TIDYING
   When pool is empty
TIDYING -> TERMINATED
   When the terminated() hook method has completed

若线程池状态为 RUNNING,当调用 shutdown() 方法后,会变成 SHUTDOWN 状态
若线程池状态为 RUNNING 或 SHUTDOWN,当调用 shutdownNow() 方法后,会变成 STOP 状态
若线程池状态为 SHUTDOWN,当线程池不存在线程,且任务队列为空时,会变成 TIDYING 状态
若线程池状态为 STOP,当线程池不存在线程时,会变成 TIDYING 状态
若线程池状态为 TIDYING,当 hook 方法 terminated() 执行完毕后,会变成 TERMINATED 状态

TIDYING 与 TERMINATED 状态都可以认为线程池任务已执行完毕,区别只是自定义处理的 terminated() 方法有没有执行

6.3.2. ThreadPoolExecutor.awaitTermination() 实现方式

awaitTermination() 方法中执行的操作比较简单,在循环中判断线程池状态是否变成 TERMINATED ,不满足且未超时时等待

在等待指定时长时调用 “termination.awaitNanos(nanos)”,termination 字段定义为 “Condition termination = mainLock.newCondition()”

ThreadPoolExecutor 类的 ctl 字段包含两层含义,一是当前线程池的 worker 线程数,二是当前线程池的状态

在 tryTerminate() 方法中,当通过 “ctl.compareAndSet(c, ctlOf(TIDYING, 0))” 将线程池状态修改为 TIDYING 成功时,会调用 terminated() 方法,再将线程池状态修改为 TERMINATED,再调用 termination.signalAll() 方法,此时 ThreadPoolExecutor.awaitTermination() 方法会结束等待

因此假如 awaitTermination() 方法指定的等待时间未超时,只要线程池任务执行完毕后,awaitTermination() 方法就会结束等待并返回

tryTerminate() 方法用于尝试结束线程池,在 processWorkerExit()、shutdown()、shutdownNow() 等方法中都有调用

假如在调用 shutdown()、shutdownNow() 方法时任务已执行完毕,则调用 tryTerminate() 方法时线程池能够满足结束的条件;否则会在线程池任务执行完毕后,通过 processWorkerExit() 调用 tryTerminate() 方法时,线程池才能执行结束操作

7. 线程池关闭后继续执行任务的结果

7.1. 调用 ThreadPoolExecutor.shutdown() 后继续执行任务

调用 ThreadPoolExecutor.shutdown() 方法关闭线程池后,假如继续使用该线程池执行任务,会使用拒绝策略对任务进行处理

使用不同的拒绝策略时,尝试执行的任务都不会被执行,区别在于当前操作是否会抛出异常:

拒绝策略处理方式
AbortPolicy抛出 RejectedExecutionException 异常
CallerRunsPolicy不执行处理
DiscardOldestPolicy不执行处理
DiscardPolicy不执行处理

7.2. 调用 ThreadPoolExecutor.shutdownNow() 后继续执行任务

同上

7.3. 代码分析

7.3.1. 线程池关闭操作

- ThreadPoolExecutor.shutdown() 方法

代码注释中说明,当前方法调用后不会再接收新的任务:

but no new tasks will be accepted.

shutdown() 方法中会调用 “ advanceRunState(SHUTDOWN) ” 方法,该方法中会将 ctl 字段代表线程池状态的值修改为 SHUTDOWN

- ThreadPoolExecutor.shutdownNow() 方法

shutdownNow() 方法中会调用 “ advanceRunState(STOP) ” 方法,该方法中会将 ctl 字段代表线程池状态的值修改为 STOP

7.3.2. 线程池关闭后执行操作

  • ThreadPoolExecutor.execute() 方法

代码注释中说明,当线程池已经被关闭时,execute() 方法无法提交任务以执行,任务会被当前的拒绝策略处理:

If the task cannot be submitted for execution, either because this executor has been shutdown or because its capacity has been reached, the task is handled by the current RejectedExecutionHandler.

3. If we cannot queue task, then we try to add a new thread.  If it fails, we know we are shut down or saturated and so reject the task.

对于以下代码块,由于 addWorker() 会返回 false,因此以下 return 不会执行,会继续执行后续代码

        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }

对于以下代码块,由于当前线程池的状态已经是 SHUTDOWN(shutdown() 方法执行后) 或 STOP(shutdownNow() 方法执行后),无法满足 isRunning() 方法中小于 SHUTDOWN 的判断,因此以下 if 条件判断不满足

if (isRunning(c) && workQueue.offer(command)) {

对于以下代码块,满足 else 条件,且 addWorker() 方法会返回 false,因此会执行 reject() 方法

        else if (!addWorker(command, false))
            reject(command);
  • ThreadPoolExecutor.submit() 方法

调用了 execute() 方法

  • ThreadPoolExecutor.reject() 方法

调用了 handler 字段的 rejectedExecution() 方法,即当前线程池的拒绝策略 RejectedExecutionHandler

  • ThreadPoolExecutor.addWorker() 方法

代码注释中说明,当线程池已停止(stop),或有资格关闭(shut down)时,addWorker() 方法返回 false:

This method returns false if the pool is stopped or eligible to shut down.

对于以下代码块,rs 对应当前线程池的状态,为 SHUTDOWN 或 STOP,满足 “rs>= SHUTDOWN”

参数 1 “Runnable firstTask” 对应 execute() 方法的参数 1 “ Runnable command ” ,execute() 方法有检查该参数不允许为 null,因此后续的 “firstTask == null” 不满足,“! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())” 满足,因此会返回 false

            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs>= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

7.3.3. 拒绝策略

- AbortPolicy.rejectedExecution() 方法

固定抛出 RejectedExecutionException 异常

  • CallerRunsPolicy.rejectedExecution() 方法

仅当线程池满足 isShutdown() 方法返回 false 时,使用调用的线程执行任务

由于线程池已被关闭,isShutdown() 方法返回 true,因此什么也不做

  • DiscardOldestPolicy.rejectedExecution() 方法

仅当线程池满足 isShutdown() 方法返回 false 时,将线程池最老的任务丢弃,再使用线程池执行任务

由于线程池已被关闭,isShutdown() 方法返回 true,因此什么也不做

  • DiscardPolicy.rejectedExecution() 方法

什么也不做

8. 线程池关闭后活跃线程结束方式

创建 ThreadPoolExecutor 后,假如调用 execute()、submit() 方法执行过任务,或者调用 prestartAllCoreThreads() 方法创建核心线程数的线程,ThreadPoolExecutor 会执行创建线程的操作

8.1. ThreadPoolExecutor.runWorker()、getTask() 方法

ThreadPoolExecutor 类中执行的 worker 线程对应类为 java.util.concurrent.ThreadPoolExecutor.Worker ,实现了 Runnable 接口,在 run() 方法中调用了 ThreadPoolExecutor.runWorker() 方法

在 ThreadPoolExecutor.runWorker() 方法中,通过 “ while (task != null || (task = getTask()) != null) ” 进行循环处理, Runnable task 变量初始使用 w.firstTask 进行赋值,即当前线程初始的需要执行的任务,后续会调用 “task.run()” 以执行对应的任务

getTask() 方法用于从任务队列中获取一个用于执行的任务,可能返回 null

假如 task 为 null 时, getTask() 方法也返回 null,则循环会结束,对应的 worker 线程会结束;

假如 getTask() 方法返回非 null,则当前 worker 线程获取到任务后会继续执行,此时循环不会结束,线程不会结束

8.2. 调用 ThreadPoolExecutor.shutdown() 后活跃线程结束方式

ThreadPoolExecutor.getTask() 方法注释中说明,当线程池状态为 SHUTDOWN ,且任务队列为空时,当前方法会返回 null:

or returns null if this worker must exit because of any of:
3. The pool is shutdown and the queue is empty.

getTask() 方法中循环里的第一段 if 代码块如下,满足时会返回 null

            if (rs>= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

调用 ThreadPoolExecutor.shutdown() 后,线程池状态会变成 SHUTDOWN

当任务队列中的任务都执行完毕时,任务队列为空

此时能够满足以上条件,getTask() 方法会返回 null

runWorker() 方法的循环会结束,活跃线程会结束

8.3. 调用 ThreadPoolExecutor.shutdownNow() 后活跃线程结束方式

ThreadPoolExecutor.getTask() 方法注释中说明,当前方法会返回 null 的另一种条件是,线程池状态为 STOP:

2. The pool is stopped.

调用 ThreadPoolExecutor.shutdownNow() 后,线程池状态会变成 STOP

满足 getTask() 方法的以上 if 判断条件,返回 null

后续步骤相同

9. 线程池关闭后空闲线程结束方式

9.1. ThreadPoolExecutor.getTask() 方法 - 第二段 if 代码块(空闲线程不会执行到)

在 getTask() 方法中,第二段 if 代码块也可能返回 null

线程池关闭后,空闲线程不会满足第二段 if 代码块的条件

ThreadPoolExecutor.getTask() 方法注释中说明,当前方法会返回 null 的其他条件如下:

worker 线程数超过线程池的最大线程数 maximumPoolSize(因为调用 setMaximumPoolSize 方法调小最大线程数)
当前 worker 线程等待任务时超时,且超时的 worker 线程可能会终止(allowCoreThreadTimeOut || workerCount> corePoolSize), 且队列非空或当前 worker 线程不是线程池中的最后一个线程(线程池中的线程数大于 1)
1. There are more than maximumPoolSize workers (due to a call to setMaximumPoolSize).
4. This worker timed out waiting for a task, and timed-out workers are subject to termination (that is, allowCoreThreadTimeOut || workerCount> corePoolSize) both before and after the timed wait, and if the queue is non-empty, this worker is not the last thread in the pool.

在 getTask() 方法中,第二段 if 代码块如下,与以上注释说明的一致:

            int wc = workerCountOf(c);

            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            if ((wc> maximumPoolSize || (timed && timedOut))
                && (wc> 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

9.2. ThreadPoolExecutor.getTask() 方法 - 阻塞获取任务

空闲线程不会执行到 ThreadPoolExecutor.getTask() 方法的第二段 if 代码块,因此会执行后续从任务队列获取任务的处理

在调用 ThreadPoolExecutor.shutdown() 之前,假如线程池中有空闲的 worker 线程执行,则调用 getTask() 方法时会阻塞在从任务队列获取任务的阶段,相关代码如下:

            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
  • 从任务队列获取任务的操作

当线程池允许核心线程超时(allowCoreThreadTimeOut 字段为 true),或 worker 线程数超过核心线程数时,timed 变量为 true 时,会调用任务队列 workQueue 的 poll(long timeout, TimeUnit unit) 方法获取任务,指定超时时间为线程池的线程超时时间( keepAliveTime 字段);

若不满足以上条件,则调用任务队列 workQueue 的 take() 方法获取任务,阻塞等待直到获得任务

  • 从任务队列获取任务的结果

当调用 poll() 或 take() 方法从任务队列获取到任务时,返回当前任务;

当调用 poll() 或 take() 方法被中断时(对应 catch InterruptedException 的处理),设置 timedOut 变量为 false,继续循环处理;

若调用 pool() 方法超时,则设置 timedOut 变量为 true,继续循环处理

9.3. 调用 ThreadPoolExecutor.shutdown() 后空闲线程结束方式

假如线程池设置为允许核心线程超时,则 worker 线程会调用 getTask() 方法中任务队列的 poll() 方法获取任务,当任务队列为空时,达到指定的超时时间后会结束等待(或因为下一种情况被中断导致结束等待);

假如线程池设置为不允许核心线程超时,则 worker 线程会调用 getTask() 方法中任务队列的 take() 方法阻塞等待获取任务

ThreadPoolExecutor.shutdown() 方法中会调用 interruptIdleWorkers() 方法,会中断空闲的线程,因此阻塞等待的 worker 线程会因 InterruptedException 异常结束 take() 方法的调用

因此无论哪种情况下,调用 ThreadPoolExecutor.shutdown() 后,空闲线程从任务队列获取任务的操作都会结束,进入下一次循环

执行 getTask() 方法中循环里的第一段 if 代码块时,与活跃线程一样,getTask() 方法会终止循环,返回 null

后续空闲线程会结束

9.4. 调用 ThreadPoolExecutor.shutdownNow() 后空闲线程结束方式

ThreadPoolExecutor.shutdownNow() 方法中会调用 interruptWorkers() 方法,会中断所有线程,包括空闲的线程

后续与调用 shutdown() 的情况一样,空闲线程会结束

10. 线程池关闭后线程执行出现异常结束后是否会创建新线程

10.1. 线程池关闭前线程执行出现异常结束后创建新线程处理

10.1.1. ThreadPoolExecutor.runWorker() 方法

在 ThreadPoolExecutor.runWorker() 方法中,通过 task.run() 执行任务时,有进行异常处理,但在 catch 中有重新抛出异常,因此 runWorker() 方法中的 while 循环在当前处理完毕后会结束,当前线程会结束运行;在最外层的 finally 中会执行 processWorkerExit() 方法

在 runWorker() 方法中有定义 completedAbruptly 变量,初始为 true;仅当 while 循环正常执行完毕时,completedAbruptly 变量被赋值为 false;若 task.run() 执行任务时出现异常,completedAbruptly 变量值保持为 true

调用 processWorkerExit() 方法时,参数 2 使用 completedAbruptly 变量

10.1.2. ThreadPoolExecutor.processWorkerExit() 方法

ThreadPoolExecutor.processWorkerExit() 方法的注释说明,当 worker 线程在执行用户任务出现异常而退出时,当前方法会替换 worker 线程:

replaces the worker if either it exited due to user task exception
        int c = ctl.get();
        if (runStateLessThan(c, STOP)) {
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }

processWorkerExit() 方法参数 2 为 completedAbruptly,注释为 “if the worker died due to user exception”,即代表当前 worker 线程是否因为执行用户任务出现异常而退出

processWorkerExit() 方法有判断 “ if (runStateLessThan(c, STOP)) ”,即要求当前线程池状态小于 STOP 时,才执行后续的操作

若 completedAbruptly 为 false,且满足 “workerCountOf© >= min ”,即当前 worker 数量大于等于预期的最小值时,执行 return,不执行后续操作

若 completedAbruptly 为 true,则调用 addWorker() 方法创建 worker 线程

当 worker 线程因为执行用户任务出现异常导致退出时,调用 processWorkerExit() 方法时参数 completedAbruptly 为 true,只要线程池状态小于 STOP,就会调用 addWorker() 方法创建 worker 线程

10.2. 调用 ThreadPoolExecutor.shutdown() 后线程执行出现异常结束后是否会创建新线程

假如使用 ThreadPoolExecutor 执行的用户任务中出现异常导致 worker 线程退出,在调用 ThreadPoolExecutor.shutdown() 关闭线程池后,此时线程池状态为 SHUTDOWN,小于 STOP,会继续创建 worker 线程

调用 ThreadPoolExecutor.shutdown() 后, 任务队列中的任务还需要继续执行,因此需要继续创建 worker 线程(异常结束时)

10.3. 调用 ThreadPoolExecutor.shutdownNow() 后线程执行出现异常结束后是否会创建新线程

假如使用 ThreadPoolExecutor 执行的用户任务中出现异常导致 worker 线程退出,在调用 ThreadPoolExecutor.shutdownNow() 关闭线程池后,此时线程池状态为 STOP,不会继续创建 worker 线程

调用 ThreadPoolExecutor.shutdownNow() 后,任务队列中的任务已经丢弃,因此不需要继续创建 worker 线程

11. 线程池停止与 JVM 退出(应用停止)的关系

java.lang.Thread#setDaemon() 方法注释中说明,当所有运行的线程都是 daemon 线程时,JVM 会退出:

The Java Virtual Machine exits when the only threads running are all daemon threads.

即只要有一个非 daemon 线程在执行,JVM 就不会退出

默认情况下线程是非 daemon,如 main 线程

11.1. 应用停止阶段不关闭线程池对 JVM 退出的影响

在应用停止时,需要关闭线程池,使对应的线程结束(包括未使用线程池的线程)

假如应用停止时有非 daemon 线程未关闭,则 JVM 不会自动退出,需要使用 kill -9 等方式结束进程

例如通过线程池创建的非 daemon 线程,且线程池不允许核心线程超时,假如不调用线程池的关闭操作,则这些线程会一直运行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值