2017-08-09 36 views
0

使用案例:每次需要处理作业时创建一个新线程。Java ExecutorService - 处于等待状态的线程

当前实现:我正在使用具有固定大小线程池的Executor服务,比如说50.对于每项工作,我都会向执行程序服务提交一个新线程。

问题:一旦作业完成,线程不会死亡并进入等待状态。 (在sun.misc.unsafe.park等待)

分析:根据此链接(WAITING at sun.misc.Unsafe.park(Native Method))和网络上的其他源,这是一个有效的场景,并且线程进入等待状态,等待某个任务被给予他们。

问题:从Java任务控制中,我能够推断出线程没有使用任何资源并且没有死锁。这很好。但是考虑提交大量作业并且池中的所有50个线程都被实例化的时间框架。之后,即使作业提交率可能下降,所有50个线程都将保持活跃状态​​。我也无法关闭执行者服务,因为它需要一直活着等待作业提交。 如果我创建了普通线程,我会发现线程在他们的工作完成后就死掉了。但是在这种情况下,创建线程的最大数量时没有制表符。因此,在高峰时间,我们可能会遇到创建的线程多于JVM可以处理的情况。

如何以最佳方式处理这种情况。我们应该忽略处于等待状态的线程还是应该去执行其他任何实现。

我试图实现的行为更像是自动缩放。在高峰期间跨越更多的服务器(在这种情况下是线程)。并且在负载不高时终止额外的服务器并保持最低的服务器数量。

+0

这不就是一个对象池的预期行为? – 11thdimension

+0

它可能是线程池的默认行为。但就我而言,所有线程在高峰时间都处于活动状态,在此之后,它们始终处于等待状态,之后只有少数几个将被使用。所以想知道是否有任何可以使用的Executor服务的其他设置。或者任何可以解决这种情况的其他实现。 – samo

+0

如果你想为每个作业创建一个新的线程,为什么你完全使用'ExecutorService'?如果你不这样做,为什么你说的第一件事是? – EJP

回答

1

之后,即使作业提交率可能下降,所有50个线程都将处于活动状态。我也无法关闭执行者服务,因为它需要一直活着等待作业提交。

...

怎么能这种情况下的最好的方式来处理。我们应该忽略处于等待状态的线程还是应该去执行其他任何实现。

我觉得答案是,你应该忽略它们。线程现在非常高效,当然有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); 
    } 
}); 
1

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

+0

但是我正在使用的Executors.newFixedThreadPool(int n)反过来使用带有KeepAliveTime为0的ThreadPoolExecutor。所以我们不必在实现中显式使用ThreadPoolExecutor。同样在这种情况下,当keepAliveTime为零时,我希望线程立即死亡,但情况并非如此。 – samo

1

可以使用ThreadPoolExecutor来完成。然而它不会做你期望的事情。以下构造函数可用于创建ThreadPoolExecutor

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

让我打破它的行为记录。提交任务时

  1. 如果poolSize小于corePoolSize,即使存在空闲线程,也会创建新线程。
  2. 如果poolSize等于corePoolSize,则将任务添加到队列中。在队列耗尽之前它不会创建新线程。
  3. 如果workQueue用尽,则创建新线程直至poolSize变为maximumPoolSize
  4. 如果poolSize等于maximumPoolSizeRejectedExecutionException

所以现在假设我们设定的核心大小为5,最大尺寸为10,100个提交任务。如果我们使用Executors类创建池对象,则不会发生任何事情。因为它创建的池使用LinkedBlockingQueue,默认的构造函数将队列容量设置为Integer.MAX_VALUE2147483647)。

以下是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发送任务。但是,这似乎很多工作没有相同的收益。

相关问题