简体   繁体   English

Java ExecutorService - 处于等待状态的线程

[英]Java ExecutorService - threads in waiting state

Use Case: Create a new thread every time I need to process a job.用例:每次需要处理作业时创建一个新线程。

Current Implementation: I am using Executor Service with fixed size thread pool, of say 50. For every job, I submit a new thread to executor service.当前实现:我使用 Executor Service 和固定大小的线程池,比如 50。对于每个作业,我向执行程序服务提交一个新线程。

Issue: Once the job is completed, the threads do not die and go into waiting state.问题:作业完成后,线程不会死亡并进入等待状态。 (waiting at sun.misc.unsafe.park) (在 sun.misc.unsafe.park 等待)

Analysis: As per this link ( WAITING at sun.misc.Unsafe.park(Native Method) ) and other sources on the net, this is a valid scenario, and the threads go into waiting state, waiting for some task to be given to them.分析:根据这个链接( WAITING at sun.misc.Unsafe.park(Native Method) )和网络上的其他来源,这是一个有效的场景,线程进入等待状态,等待一些任务被分配给他们。

Question: From Java mission control, I am able to deduce that the threads are not using any resources and are not in dead lock.问题:从 Java 任务控制中,我能够推断出线程没有使用任何资源并且没有处于死锁状态。 So that is good.所以这很好。 But consider a time frame when lots of jobs are submitted and all 50 threads in the pool were instantiated.但请考虑提交大量作业并且池中的所有 50 个线程都已实例化的时间范围。 And after that all the 50 threads are going to be alive even though the job submission rate might have decreased.之后,即使作业提交率可能下降,所有 50 个线程都将处于活动状态。 I cannot shut down the executor service also, since it needs to be alive forever waiting for jobs to be submitted.我也不能关闭 executor 服务,因为它需要永远活着等待提交作业。 If I create, normal threads, I see that the threads die after their job is done.如果我创建普通线程,我会看到线程在完成工作后死亡。 But in that case there is no tab in the max amount of threads being created.但在这种情况下,正在创建的最大线程数中没有选项卡。 So in a peak time, we may run into a situation where more threads are created than what the JVM can handle.因此,在高峰时间,我们可能会遇到创建的线程数超过 JVM 可以处理的线程数的情况。

How can this scenario be handled in the best possible way.如何以最好的方式处理这种情况。 Should we ignore the threads being in the waiting state or should I go for any other implementation.我们应该忽略处于等待状态的线程还是应该进行任何其他实现。

The behavior that I am trying to implement is more like autoscaling.我试图实现的行为更像是自动缩放。 Span more servers(threads in this case)during peak time.在高峰时间跨越更多的服务器(在这种情况下是线程)。 And terminate the extra servers and maintain a minimum server count when the load is not that high.并在负载不那么高时终止额外的服务器并保持最少的服务器数量。

使用ThreadPoolExecutor并通过其任一构造函数设置其keepAliveTime属性。

It can be done using ThreadPoolExecutor .可以使用ThreadPoolExecutor来完成。 However it won't do what you expect it to do.但是它不会做你期望它做的事情。 Following constructor can be used to create a ThreadPoolExecutor .以下构造函数可用于创建ThreadPoolExecutor

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)

Let me break down it's behavior as documented.让我分解它记录的行为。 When a task is submitted提交任务时

  1. If poolSize is less than corePoolSize , a new thread is created, even if there are idle threads.如果poolSize小于corePoolSize ,则会创建一个新线程,即使有空闲线程也是如此。
  2. If poolSize is equal to the corePoolSize then task is added to the queue.如果poolSize等于corePoolSize则将任务添加到队列中。 It won't create new threads until queue is exhausted.在队列耗尽之前它不会创建新线程。
  3. if workQueue is exhausted then new thread is created till poolSize becomes maximumPoolSize .如果workQueue耗尽然后创建新的线程,直到poolSize变得maximumPoolSize
  4. If poolSize is equal to the maximumPoolSize throw RejectedExecutionException如果poolSize等于maximumPoolSize抛出RejectedExecutionException

So now suppose we set core size to 5, max size to 10 and submit 100 tasks.所以现在假设我们将核心大小设置为 5,最大大小设置为 10 并提交 100 个任务。 Nothing will happen if we created our pool object with the Executors class.如果我们使用Executors类创建池对象,则不会发生任何事情。 As pools created by it use LinkedBlockingQueue , with default constructor, which sets the queue capacity to measly Integer.MAX_VALUE ( 2147483647 ).由于它创建的池使用LinkedBlockingQueue ,带有默认构造函数,它将队列容量设置为微不足道的Integer.MAX_VALUE ( 2147483647 )。

Following is the code from Executors以下是Executors的代码

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

Default constructor in LinkedBlockingQueue LinkedBlockingQueue默认构造函数

public LinkedBlockingQueue() {
    this(Integer.MAX_VALUE);
}
public LinkedBlockingQueue(int capacity) {
...

Option to create ThreadPoolExecutor directly still remains, however that's not much helpful.直接创建ThreadPoolExecutor选项仍然存在,但这并没有多大帮助。 Lets examine that.让我们检查一下。 Suppose we create ThreadPoolExecutor object using following code.假设我们使用以下代码创建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);

Where MAX_QUEUE_SIZE is 10. Number of maximum task that can be submitted can be found by following formula.其中MAX_QUEUE_SIZE为 10。可以提交的最大任务数可以通过以下公式找到。

MAX_TASKS = MAX_POOL_SIZE + WORK_QUEUE_CAPACITY

So if max pool size is 10 and work queue size is also 10, then 21st task will be rejected, if no threads are free.因此,如果最大池大小为 10 并且工作队列大小也为 10,那么如果没有空闲线程,则第 21 个任务将被拒绝。

It's important to keep in mind that it doesn't give us desired behavior.重要的是要记住它不会给我们想要的行为。 As threads are only killed if there are more threads than corePoolSize .因为线程只有在线程数超过corePoolSize才会被杀死。 Thread pool increases more than corePoolSize only if workQueue has been exhausted.仅当workQueue已耗尽时,线程池增加的幅度超过corePoolSize

So maxPoolSize is a failsafe option to avoid queue exhaustion.因此maxPoolSize是避免队列耗尽的故障安全选项。 Not the other way around.不是反过来。 Max pool size is not intended to kill idle threads.最大池大小不是为了杀死空闲线程。

If we set queue size too small, we risk task rejection.如果我们将队列大小设置得太小,就会有任务被拒绝的风险。 If we set it too high, poolSize will never cross corePoolSize .如果我们将其设置得太高, poolSize将永远不会poolSize corePoolSize

May be you can explore ThreadPoolExecutor.setRejectedExecutionHandler .也许你可以探索ThreadPoolExecutor.setRejectedExecutionHandler And save rejected task a separate queue which will send tasks to workQueue once workQueue.capacity becomes less than the max capacity periodically.并将被拒绝的任务保存在一个单独的队列中,一旦workQueue.capacity定期小于最大容量,该队列就会将任务发送到workQueue But that seems a lot of work without equivalent gain.但这似乎是很多工作,没有等效的收益。

And after that all the 50 threads are going to be alive even though the job submission rate might have decreased.之后,即使作业提交率可能下降,所有 50 个线程都将处于活动状态。 I cannot shut down the executor service also, since it needs to be alive forever waiting for jobs to be submitted.我也不能关闭 executor 服务,因为它需要永远活着等待提交作业。

... ...

How can this scenario be handled in the best possible way.如何以最好的方式处理这种情况。 Should we ignore the threads being in the waiting state or should I go for any other implementation.我们应该忽略处于等待状态的线程还是应该进行任何其他实现。

I think the answer is, you should ignore them.我认为答案是,你应该忽略它们。 Threads are pretty darn efficient these days and certainly 50 dormant threads aren't going to affect the runtime of your application in any way.线程现在非常高效,当然 50 个休眠线程不会以任何方式影响应用程序的运行时。 It would be different if you are talking about a much large number of threads or a whole series of different thread pools all hanging around.如果您谈论的是大量线程或一系列不同的线程池,则情况会有所不同。

That said, as mentioned, if you want your threads to timeout then you will need to specify a different "core" number of threads (how many should always be running) than the "max" (maximum number that the pool can grow too) and how long the threads should start dormant before they should exit to keep the thread count down at the "core" number.也就是说,如上所述,如果您希望线程超时,则需要指定与“最大”(池也可以增长的最大数量)不同的“核心”线程数(应该始终运行多少)以及线程在退出之前应该开始休眠多长时间以将线程计数保持在“核心”数。 The problem with this is that then you'll need to have a fixed size job queue otherwise the 2nd thread will never be created.这样做的问题是,您需要有一个固定大小的作业队列,否则将永远不会创建第二个线程。 That's (unfortunately) how the ThreadPoolExecutor works.这就是(不幸的是) ThreadPoolExecutor工作方式。

If you have a different core and max thread numbers and you are submitting a large number of jobs to your thread-pool then you'll need to block the producer else the jobs will be rejected by the queue if it fills up.如果您有不同的核心和最大线程数,并且您正在向线程池提交大量作业,那么您需要阻止生产者,否则如果队列填满,作业将被队列拒绝。

Something like:类似的东西:

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);
     }
});

better to shutdown the executors once it's done.最好在完成后关闭执行程序。 it will release all the threads which are created with executor Service.它将释放所有使用 executor Service 创建的线程。

finally {
        if(!executors.isShutdown())
            executors.shutdown();
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM