Java ThreadPoolExecutor:动态更新核心池大小会间歇性地拒绝传入任务
我遇到了这样一个问题:如果我试图在创建池后将Java ThreadPoolExecutor:动态更新核心池大小会间歇性地拒绝传入任务,java,multithreading,Java,Multithreading,我遇到了这样一个问题:如果我试图在创建池后将ThreadPoolExecutor的核心池大小调整为不同的数字,然后间歇性地,即使我提交的任务数从未超过queueSize+maxPoolSize个,但仍会使用RejectedExecutionException拒绝某些任务 我试图解决的问题是扩展ThreadPoolExecutor,它根据线程池队列中挂起的执行调整其核心线程的大小。我需要这个,因为默认情况下,只有当队列已满时,ThreadPoolExecutor才会创建一个新的Thread 下面是
ThreadPoolExecutor
的核心池大小调整为不同的数字,然后间歇性地,即使我提交的任务数从未超过queueSize+maxPoolSize
个,但仍会使用RejectedExecutionException
拒绝某些任务
我试图解决的问题是扩展ThreadPoolExecutor
,它根据线程池队列中挂起的执行调整其核心线程的大小。我需要这个,因为默认情况下,只有当队列已满时,ThreadPoolExecutor
才会创建一个新的Thread
下面是一个小型自包含的纯Java8程序,它演示了这个问题
import static java.lang.Math.max;
import static java.lang.Math.min;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolResizeTest {
public static void main(String[] args) throws Exception {
// increase the number of iterations if unable to reproduce
// for me 100 iterations have been enough
int numberOfExecutions = 100;
for (int i = 1; i <= numberOfExecutions; i++) {
executeOnce();
}
}
private static void executeOnce() throws Exception {
int minThreads = 1;
int maxThreads = 5;
int queueCapacity = 10;
ThreadPoolExecutor pool = new ThreadPoolExecutor(
minThreads, maxThreads,
0, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(queueCapacity),
new ThreadPoolExecutor.AbortPolicy()
);
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> resizeThreadPool(pool, minThreads, maxThreads),
0, 10, TimeUnit.MILLISECONDS);
CompletableFuture<Void> taskBlocker = new CompletableFuture<>();
try {
int totalTasksToSubmit = queueCapacity + maxThreads;
for (int i = 1; i <= totalTasksToSubmit; i++) {
// following line sometimes throws a RejectedExecutionException
pool.submit(() -> {
// block the thread and prevent it from completing the task
taskBlocker.join();
});
// Thread.sleep(10); //enabling even a small sleep makes the problem go away
}
} finally {
taskBlocker.complete(null);
scheduler.shutdown();
pool.shutdown();
}
}
/**
* Resize the thread pool if the number of pending tasks are non-zero.
*/
private static void resizeThreadPool(ThreadPoolExecutor pool, int minThreads, int maxThreads) {
int pendingExecutions = pool.getQueue().size();
int approximateRunningExecutions = pool.getActiveCount();
/*
* New core thread count should be the sum of pending and currently executing tasks
* with an upper bound of maxThreads and a lower bound of minThreads.
*/
int newThreadCount = min(maxThreads, max(minThreads, pendingExecutions + approximateRunningExecutions));
pool.setCorePoolSize(newThreadCount);
pool.prestartAllCoreThreads();
}
}
导入静态java.lang.Math.max;
导入静态java.lang.Math.min;
导入java.util.concurrent.CompletableFuture;
导入java.util.concurrent.Executors;
导入java.util.concurrent.LinkedBlockingQueue;
导入java.util.concurrent.ScheduledExecutorService;
导入java.util.concurrent.ThreadPoolExecutor;
导入java.util.concurrent.TimeUnit;
公共类ThreadPoolResizeTest{
公共静态void main(字符串[]args)引发异常{
//如果无法复制,请增加迭代次数
//对我来说,100次迭代已经足够了
int numberOfExecutions=100;
对于(int i=1;i resizeThreadPool(pool、minThreads、maxThreads),
0,10,时间单位为毫秒);
CompletableFuture taskBlocker=新的CompletableFuture();
试一试{
int totalTasksToSubmit=queueCapacity+maxThreads;
对于(int i=1;i{
//阻止线程并阻止它完成任务
taskBlocker.join();
});
//Thread.sleep(10);//即使启用一个小的睡眠,问题也会消失
}
}最后{
taskBlocker.complete(空);
scheduler.shutdown();
pool.shutdown();
}
}
/**
*如果挂起的任务数不为零,请调整线程池的大小。
*/
私有静态void resizeThreadPool(ThreadPoolExecutor池、int-minThreads、int-maxThreads){
int pendingExecutions=pool.getQueue().size();
int approxiserunningexecutions=pool.getActiveCount();
/*
*New core thread count应该是挂起任务和当前执行任务的总和
*具有maxThreads的上限和minThreads的下限。
*/
int newThreadCount=min(最大线程数,最大线程数(最小线程数,挂起执行数+近似执行数));
setCorePoolSize(newThreadCount);
pool.prestartAllCoreThreads();
}
}
如果我提交的数据从未超过queueCapacity+maxThreads,那么池为什么要抛出RejectedExecutionException呢。我从不更改最大线程数,因此根据ThreadPoolExecutor的定义,它应该在线程中或队列中容纳任务
当然,如果我从不调整池的大小,那么线程池就不会拒绝任何提交。这也很难调试,因为在提交中添加任何类型的延迟都会使问题消失
关于如何修复RejectedExecutionException的任何提示?以下是发生这种情况的原因: 在我的示例中,我使用minThreads=0、maxThreads=2和queueCapacity=2来缩短它。 第一个命令被提交,这在execute方法中完成:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
我认为要解决这个问题,您应该将队列的容量设置为您希望执行的命令的最大值。不确定这是否符合错误。这是在队列已满后创建额外工作线程时的行为,但java文档中已经注意到,调用方必须处理被拒绝的任务 Java文档 新螺纹的工厂。所有线程都是使用此方法创建的 工厂(通过方法addWorker)。所有来电者必须做好准备 使addWorker失败,这可能反映系统或用户的 限制线程数的策略。即使不是 视为错误,未能创建线程可能会导致 新任务被拒绝或现有任务仍停留在 排队 当您调整核心池大小时,比如说增加,将创建额外的工作线程(
setCorePoolSize
中的addWorker
方法),并且当addWorker
返回false时,创建额外工作线程的调用(addWorker
方法fromexecute
)将被拒绝(add Worker
last code snippet),因为setCorePoolSize
已经创建了足够多的额外Worker,但尚未运行以反映队列中的更新
相关部分
比较
public void setCorePoolSize(int corePoolSize) {
....
int k = Math.min(delta, workQueue.size());
while (k-- > 0 && addWorker(null, true)) {
if (workQueue.isEmpty())
break;
}
}
public void execute(Runnable command) {
...
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
private boolean addWorker(Runnable firstTask, boolean core) {
....
if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
return false;
}
public void setCorePoolSize(int corePoolSize){
....
int k=Math.min(delta,workQueue.size());
while(k-->0&&addWorker(null,true)){
if(workQueue.isEmpty())
打破
}
}
public void execute(Runnable命令){
...
int c=ctl.get();
if(工作计数(c)=容量| | wc>=(核心?核心池大小:最大池大小))
返回false;
}
使用自定义重试拒绝执行处理程序(这应该适用于您的案例,因为您有更高的bo)
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@24c22fe rejected from java.util.concurrent.ThreadPoolExecutor@cd1e646[Running, pool size = 2, active threads = 2, queued tasks = 2, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at ThreadPoolResizeTest.executeOnce(ThreadPoolResizeTest.java:60)
at ThreadPoolResizeTest.runTest(ThreadPoolResizeTest.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:69)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:48)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:292)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:365)
public void setCorePoolSize(int corePoolSize) {
....
int k = Math.min(delta, workQueue.size());
while (k-- > 0 && addWorker(null, true)) {
if (workQueue.isEmpty())
break;
}
}
public void execute(Runnable command) {
...
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
private boolean addWorker(Runnable firstTask, boolean core) {
....
if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
return false;
}
public static class RetryRejectionPolicy implements RejectedExecutionHandler {
public RetryRejectionPolicy () {}
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
while(true)
if(e.getQueue().offer(r)) break;
}
}
}
ThreadPoolExecutor pool = new ThreadPoolExecutor(
minThreads, maxThreads,
0, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(queueCapacity),
new ThreadPoolResizeTest.RetryRejectionPolicy()
);