Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/307.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java ThreadPoolExecutor:动态更新核心池大小会间歇性地拒绝传入任务_Java_Multithreading - Fatal编程技术网

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
方法from
execute
)将被拒绝(
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()
 );