Java ExecutorService - 处于等待状态的线程
Posted
技术标签:
【中文标题】Java ExecutorService - 处于等待状态的线程【英文标题】:Java ExecutorService - threads in waiting state 【发布时间】:2018-01-17 23:16:27 【问题描述】:用例:每次我需要处理作业时都创建一个新线程。
当前实现:我正在使用具有固定大小线程池的执行器服务,例如 50 个。对于每个作业,我都会向执行器服务提交一个新线程。
问题:一旦作业完成,线程不会死亡并进入等待状态。 (在 sun.misc.unsafe.park 等待)
分析:根据这个链接(WAITING at sun.misc.Unsafe.park(Native Method))和网上的其他来源,这是一个有效的场景,线程进入等待状态,等待一些任务给他们。
问题:从 Java 任务控制中,我能够推断出线程没有使用任何资源并且没有处于死锁状态。所以这很好。但是考虑一个提交大量作业并且池中所有 50 个线程都被实例化的时间范围。在那之后,所有 50 个线程都将处于活动状态,即使作业提交率可能已经下降。我也无法关闭执行程序服务,因为它需要永远活着等待提交作业。 如果我创建普通线程,我会看到线程在完成工作后死亡。但在这种情况下,正在创建的最大线程数中没有选项卡。因此,在高峰期,我们可能会遇到创建的线程数超过 JVM 可以处理的线程数的情况。
如何以最佳方式处理这种情况。我们应该忽略处于等待状态的线程还是应该进行任何其他实现。
我试图实现的行为更像是自动缩放。在高峰时间跨越更多服务器(在这种情况下为线程)。并在负载不那么高时终止额外的服务器并保持最少的服务器数量。
【问题讨论】:
这不是对象池的预期行为吗? 这可能是线程池的默认行为。但在我的情况下,所有线程在高峰时间都处于活动状态,之后它们总是处于等待状态,之后一次只会使用很少的线程。所以想知道 Executor Service 是否还有其他设置可以使用。或任何其他可以解决这种情况的实现。 如果您希望每个作业都有一个新线程,为什么要使用ExecutorService
?如果你不这样做,你为什么要说第一句话?
@EJP.. 如果我要为每个作业创建一个新线程,使用 new Thread(new My Runnable()).start.. 那么我如何密切关注峰值期间创建的最大线程数次。不会出现在达到最大资源时抛出异常的情况。这就是我尝试使用并发包提供的线程创建框架的原因。我的要求是通过创建线程来处理所有作业,但如果线程在“n”毫秒内没有收到任何作业,则终止线程。更像是自动缩放功能,您可以在高峰时段跨越更多服务器,并在不需要时关闭它们。
【参考方案1】:
之后所有 50 个线程都将处于活动状态,即使作业提交率可能已经下降。我也无法关闭执行程序服务,因为它需要永远活着等待提交作业。
...
如何以最佳方式处理这种情况。我们应该忽略处于等待状态的线程还是应该进行任何其他实现。
我认为答案是,你应该忽略它们。这些天线程非常高效,当然 50 个休眠线程不会以任何方式影响应用程序的运行时。如果您谈论的是大量线程或一系列不同的线程池,那就不同了。
也就是说,如上所述,如果您希望线程超时,那么您需要指定与“max”(池可以运行的最大数量)不同的“核心”线程数(应该始终运行多少)增长)以及线程应该在退出之前开始休眠多长时间以保持线程在“核心”数处倒计时。这样做的问题是,您需要有一个固定大小的作业队列,否则将永远不会创建第二个线程。这就是(不幸的是)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);
);
【讨论】:
您好,请问这 (***.com/q/59231602) 是否相关?【参考方案2】:使用ThreadPoolExecutor 并通过其任一构造函数设置其keepAliveTime
属性。
【讨论】:
但是我正在使用的 Executors.newFixedThreadPool(int n) 反过来使用 KeepAliveTime 为 0 的 ThreadPoolExecutor。所以我们不必在实现中显式使用 ThreadPoolExecutor。同样在这种情况下,当 keepAliveTime 为零时,我希望线程立即死亡,但事实并非如此。【参考方案3】:可以使用ThreadPoolExecutor 来完成。但是它不会做你期望它做的事情。以下构造函数可用于创建ThreadPoolExecutor
。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)
让我分解它的行为记录。提交任务时
-
如果
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
将永远不会超过corePoolSize
。
也许你可以探索ThreadPoolExecutor.setRejectedExecutionHandler。并将被拒绝的任务保存在一个单独的队列中,一旦workQueue.capacity
周期性地小于最大容量,它将向workQueue
发送任务。但这似乎需要做很多工作而没有同等的收获。
【讨论】:
【参考方案4】:一旦完成,最好关闭执行程序。 它将释放所有使用执行器服务创建的线程。
finally
if(!executors.isShutdown())
executors.shutdown();
【讨论】:
以上是关于Java ExecutorService - 处于等待状态的线程的主要内容,如果未能解决你的问题,请参考以下文章
Java 中这段代码中的 ExecutorService.submit 和 ExecutorService.execute 有啥区别?