java线程池的到达存活时间,max多余线程销毁是谁来做的
在 Java 线程池里,当线程的空闲时间超出所设置的存活时间(keepAliveTime),并且当前线程数量多于核心线程数(corePoolSize)时,超出的线程就会被销毁。这个销毁操作是由线程池本身主动完成的,确切地说,是由线程池内部的工作线程(Worker)自己执行退出操作来实现的。
下面为你详细介绍这个过程:
- Worker 类的作用:线程池中的每个工作线程都被封装成了一个 Worker 对象,而 Worker 类继承自 AbstractQueuedSynchronizer(AQS),同时实现了 Runnable 接口。
- 获取任务的方式:Worker 线程会通过
getTask()
方法从任务队列里获取任务。要是在keepAliveTime
这段时间内都没有新任务到来,getTask()
方法就会返回 null。 - 线程的退出逻辑:当
getTask()
返回 null 之后,Worker 线程的 run () 方法就会执行完毕,接着线程就会正常退出,最终被 JVM 回收。
执行主体
线程池中的工作线程(Worker)在空闲时,会自己检测是否超出了存活时间。一旦超出,就会自行退出,而不是由某个外部组件来销毁它们。
关键机制
- 任务获取逻辑:线程池中的工作线程会通过
BlockingQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
方法去尝试获取任务。要是在指定的存活时间内都没有获取到任务,这个方法就会返回 null。 - 线程退出条件:当工作线程获取任务返回 null 时,就会触发线程的退出逻辑,使得线程正常结束。
- 线程数量调整:线程池会通过原子操作来动态调整工作线程的数量,保证其不会超过核心线程数(corePoolSize)。
下面是 Java 线程池里相关的核心代码片段:
private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? for (;;) { // ... 省略部分代码 ... // 依据线程池的状态和配置,判断是否需要让线程超时退出 boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; // ... 省略部分代码 ... try { // 如果允许超时,就调用poll方法,设置等待时间 Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : // 否则调用take方法,一直阻塞等待 workQueue.take(); if (r != null) return r; // 若超时没有获取到任务,标记为超时 timedOut = true; } catch (InterruptedException retry) { timedOut = false; } // ... 省略部分代码 ... // 如果超时或者队列是空的,就尝试减少工作线程数量 if ((timedOut || (wq == 0)) && (wc > 1 || wq == 0)) { if (compareAndDecrementWorkerCount(c)) return null; continue; } } }
总结
- 执行主体:是工作线程自身通过检查空闲时间来决定是否退出。
- 触发条件:当线程数量超过核心线程数,并且空闲时间达到了 keepAliveTime。
- 线程安全:利用 CAS(Compare-And-Swap)操作来保证线程数量调整的原子性。
线程池会借助
compareAndDecrementWorkerCount
这个原子操作来减少工作线程的计数,进而保证线程安全。综上所述,Java 线程池里的线程销毁是线程池内部自动管理的,并不需要用户手动干预。