可以使用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
发送任务。但是,这似乎很多工作没有相同的收益。
这不就是一个对象池的预期行为? – 11thdimension
它可能是线程池的默认行为。但就我而言,所有线程在高峰时间都处于活动状态,在此之后,它们始终处于等待状态,之后只有少数几个将被使用。所以想知道是否有任何可以使用的Executor服务的其他设置。或者任何可以解决这种情况的其他实现。 – samo
如果你想为每个作业创建一个新的线程,为什么你完全使用'ExecutorService'?如果你不这样做,为什么你说的第一件事是? – EJP