Java 线程池执行器策略

Java 线程池执行器策略,java,multithreading,concurrency,executor,blockingqueue,Java,Multithreading,Concurrency,Executor,Blockingqueue,我正试图使用ThreadPoolExecutor来安排任务,但它的策略遇到了一些问题。以下是它的声明行为: 如果正在运行的线程少于corePoolSize,则执行器总是倾向于添加新线程,而不是排队 如果corePoolSize或多个线程正在运行,那么执行器总是倾向于将请求排队,而不是添加新线程 如果请求无法排队,则会创建一个新线程,除非该线程将超过maximumPoolSize,在这种情况下,任务将被拒绝 我想要的行为是: 同上 如果运行的线程大于corePoolSize但小于maximumPo

我正试图使用ThreadPoolExecutor来安排任务,但它的策略遇到了一些问题。以下是它的声明行为:

  • 如果正在运行的线程少于corePoolSize,则执行器总是倾向于添加新线程,而不是排队
  • 如果corePoolSize或多个线程正在运行,那么执行器总是倾向于将请求排队,而不是添加新线程
  • 如果请求无法排队,则会创建一个新线程,除非该线程将超过maximumPoolSize,在这种情况下,任务将被拒绝
  • 我想要的行为是:

  • 同上
  • 如果运行的线程大于corePoolSize但小于maximumPoolSize,则更喜欢添加新线程而不是排队,使用空闲线程而不是添加新线程
  • 同上
  • 基本上我不希望任何任务被拒绝;我希望他们在一个无限队列中排队。但我确实希望有最大的线程数。如果我使用无界队列,它在到达coreSize后就不会生成线程。如果我使用有界队列,它会拒绝任务。这有什么办法吗


    我现在想的是在SynchronousQueue上运行ThreadPoolExecutor,但不是直接将任务提供给它,而是将它们提供给一个单独的无限LinkedBlockingQueue。然后,另一个线程从LinkedBlockingQueue馈送到执行器,如果一个线程被拒绝,它将再次尝试,直到它没有被拒绝为止。不过,这似乎是一种痛苦和一种攻击——有没有更干净的方法来做到这一点?

    只需设置
    corePoolsize=maximumPoolSize
    并使用一个无边界队列

    在点列表中,1不包括2,因为
    corePoolSize
    将始终小于或等于
    maximumPoolSize

    编辑

    在您想要的和TPE将提供给您的之间仍然存在一些不兼容的地方

    如果您有一个无界队列,
    maximumPoolSize
    将被忽略,因此,正如您所观察到的,将不会创建和使用超过
    corePoolSize
    的线程


    所以,同样,如果您使用一个无限队列获取
    corePoolsize=maximumPoolSize
    ,您就得到了您想要的,不是吗

    您会寻找更像缓存线程池的东西吗


    可能不需要根据请求对线程池进行微管理

    缓存线程池将重用空闲线程,同时还允许潜在的无限并发线程。当然,这可能会导致突发期间上下文切换开销导致性能失控

    Executors.newCachedThreadPool();
    
    更好的选择是对线程总数进行限制,同时放弃确保首先使用空闲线程的概念。配置更改将是:

    corePoolSize = maximumPoolSize = N;
    allowCoreThreadTimeOut(true);
    setKeepAliveTime(aReasonableTimeDuration, TimeUnit.SECONDS);
    
    在这种情况下,如果执行器的线程数小于
    corePoolSize
    线程数,那么它一定不是很忙。如果系统不是很忙,那么启动一个新线程没有什么害处。执行此操作将导致
    ThreadPoolExecutor
    始终创建一个新的工作线程(如果该工作线程数低于允许的最大工作线程数)。只有当最大数量的工人正在“运行”时,空闲等待任务的工人才会被分配任务。如果工作人员在没有任务的情况下等待
    aReasonableTimeDuration
    ,则允许其终止。使用合理的池大小限制(毕竟,只有这么多CPU)和合理的超时(防止线程不必要地终止),很可能会看到预期的好处

    最后一个选择是草率的。基本上,
    ThreadPoolExecutor
    在内部使用
    BlockingQueue.offer
    来确定队列是否有容量。
    BlockingQueue
    的自定义实现总是可以拒绝
    提供
    尝试。当
    ThreadPoolExecutor
    未能
    向队列提供任务时,它将尝试创建一个新的辅助进程。如果无法创建新工作进程,将调用
    RejectedExecutionHandler
    。此时,自定义
    RejectedExecutionHandler
    可能会强制将
    put
    放入自定义
    BlockingQueue

    /** Hackish BlockingQueue Implementation tightly coupled to ThreadPoolexecutor implementation details. */
    class ThreadPoolHackyBlockingQueue<T> implements BlockingQueue<T>, RejectedExecutionHandler {
        BlockingQueue<T> delegate;
    
        public boolean offer(T item) {
            return false;
        }
    
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            delegate.put(r);
        }
    
        //.... delegate methods
    }
    
    /**Hackish BlockingQueue实现与ThreadPoolexecutor实现细节紧密耦合*/
    类ThreadPoolHackyBlockingQueue实现BlockingQueue,RejectedExecutionHandler{
    阻塞队列委托;
    公开发售(T项){
    返回false;
    }
    public void rejectedExecution(可运行的r、线程池执行器执行器){
    委托人。付诸表决(r);
    }
    //…委托方法
    }
    
    您的用例是常见的、完全合法的,不幸的是比预期的更难。对于背景信息,您可以阅读并找到指向解决方案的指针(也在线程中提到)。谢伊的解决方案很好用


    一般来说,我会对无界队列有点担心;通常最好有显式的传入流控制,它可以优雅地降级,并调节当前/剩余工作的比率,以不压倒生产者或消费者。

    Oops,我写的并不是我想要的。我编辑了原始设置。设置corePoolsize=maximumPoolSize确实很接近,但我也使用allowCoreThreadTimeOut(false)和prestartAllCoreThreads()。