![](/img/trans.png)
[英]ExecutorService stop and waiting to another ExecutorService to finish in Java
[英]Java ExecutorService - threads in waiting state
用例:每次需要处理作业时创建一个新线程。
当前实现:我使用 Executor Service 和固定大小的线程池,比如 50。对于每个作业,我向执行程序服务提交一个新线程。
问题:作业完成后,线程不会死亡并进入等待状态。 (在 sun.misc.unsafe.park 等待)
分析:根据这个链接( WAITING at sun.misc.Unsafe.park(Native Method) )和网络上的其他来源,这是一个有效的场景,线程进入等待状态,等待一些任务被分配给他们。
问题:从 Java 任务控制中,我能够推断出线程没有使用任何资源并且没有处于死锁状态。 所以这很好。 但请考虑提交大量作业并且池中的所有 50 个线程都已实例化的时间范围。 之后,即使作业提交率可能下降,所有 50 个线程都将处于活动状态。 我也不能关闭 executor 服务,因为它需要永远活着等待提交作业。 如果我创建普通线程,我会看到线程在完成工作后死亡。 但在这种情况下,正在创建的最大线程数中没有选项卡。 因此,在高峰时间,我们可能会遇到创建的线程数超过 JVM 可以处理的线程数的情况。
如何以最好的方式处理这种情况。 我们应该忽略处于等待状态的线程还是应该进行任何其他实现。
我试图实现的行为更像是自动缩放。 在高峰时间跨越更多的服务器(在这种情况下是线程)。 并在负载不那么高时终止额外的服务器并保持最少的服务器数量。
使用ThreadPoolExecutor并通过其任一构造函数设置其keepAliveTime
属性。
可以使用ThreadPoolExecutor来完成。 但是它不会做你期望它做的事情。 以下构造函数可用于创建ThreadPoolExecutor
。
让我分解它记录的行为。 提交任务时
poolSize
小于corePoolSize
,则会创建一个新线程,即使有空闲线程也是如此。poolSize
等于corePoolSize
则将任务添加到队列中。 在队列耗尽之前它不会创建新线程。workQueue
耗尽然后创建新的线程,直到poolSize
变得maximumPoolSize
。poolSize
等于maximumPoolSize
抛出RejectedExecutionException 所以现在假设我们将核心大小设置为 5,最大大小设置为 10 并提交 100 个任务。 如果我们使用Executors类创建池对象,则不会发生任何事情。 由于它创建的池使用LinkedBlockingQueue ,带有默认构造函数,它将队列容量设置为微不足道的Integer.MAX_VALUE ( 2147483647
)。
以下是Executors
的代码
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
LinkedBlockingQueue
默认构造函数
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
public LinkedBlockingQueue(int capacity) {
...
直接创建ThreadPoolExecutor
选项仍然存在,但这并没有多大帮助。 让我们检查一下。 假设我们使用以下代码创建ThreadPoolExecutor
对象。
ArrayBlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(MAX_QUEUE_SIZE);
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, IDLE_LIFE_TIME, TimeUnit.SECONDS, workQueue);
其中MAX_QUEUE_SIZE
为 10。可以提交的最大任务数可以通过以下公式找到。
MAX_TASKS = MAX_POOL_SIZE + WORK_QUEUE_CAPACITY
因此,如果最大池大小为 10 并且工作队列大小也为 10,那么如果没有空闲线程,则第 21 个任务将被拒绝。
重要的是要记住它不会给我们想要的行为。 因为线程只有在线程数超过corePoolSize
才会被杀死。 仅当workQueue
已耗尽时,线程池增加的幅度超过corePoolSize
。
因此maxPoolSize
是避免队列耗尽的故障安全选项。 不是反过来。 最大池大小不是为了杀死空闲线程。
如果我们将队列大小设置得太小,就会有任务被拒绝的风险。 如果我们将其设置得太高, poolSize
将永远不会poolSize
corePoolSize
。
也许你可以探索ThreadPoolExecutor.setRejectedExecutionHandler 。 并将被拒绝的任务保存在一个单独的队列中,一旦workQueue.capacity
定期小于最大容量,该队列就会将任务发送到workQueue
。 但这似乎是很多工作,没有等效的收益。
之后,即使作业提交率可能下降,所有 50 个线程都将处于活动状态。 我也不能关闭 executor 服务,因为它需要永远活着等待提交作业。
...
如何以最好的方式处理这种情况。 我们应该忽略处于等待状态的线程还是应该进行任何其他实现。
我认为答案是,你应该忽略它们。 线程现在非常高效,当然 50 个休眠线程不会以任何方式影响应用程序的运行时。 如果您谈论的是大量线程或一系列不同的线程池,则情况会有所不同。
也就是说,如上所述,如果您希望线程超时,则需要指定与“最大”(池也可以增长的最大数量)不同的“核心”线程数(应该始终运行多少)以及线程在退出之前应该开始休眠多长时间以将线程计数保持在“核心”数。 这样做的问题是,您需要有一个固定大小的作业队列,否则将永远不会创建第二个线程。 这就是(不幸的是) ThreadPoolExecutor
工作方式。
如果您有不同的核心和最大线程数,并且您正在向线程池提交大量作业,那么您需要阻止生产者,否则如果队列填满,作业将被队列拒绝。
类似的东西:
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE,
60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(MAX_QUEUE_SIZE));
// need to say what to do if the queue is full
threadPool.setRejectedExecutionHandler(new RejectedExecutionHandler() {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// this will block the caller if the queue is full
executor.getQueue().put(r);
}
});
最好在完成后关闭执行程序。 它将释放所有使用 executor Service 创建的线程。
finally {
if(!executors.isShutdown())
executors.shutdown();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.