Java 线程池执行器配置

Java 线程池执行器配置,java,Java,我有一种情况,我希望添加到线程池的速度比处理速度快。我不认为无限队列是一个好主意,因为如果不进行检查,有足够的数据,队列可能会增长到耗尽所有内存。鉴于此,我正在尝试确定ThreadPoolExecutor的正确设置 我的第一个想法是使用直接切换和调用方运行失败策略的固定线程池。但我想知道这是否会影响吞吐量(因为每次调用调用方运行策略时,线程池任务都可能会完成并闲置一段时间) 另一个想法是使用ArrayBlockingQueue固定线程池,但我实际上不确定它的行为。我希望这意味着执行者更喜欢创建小

我有一种情况,我希望添加到线程池的速度比处理速度快。我不认为无限队列是一个好主意,因为如果不进行检查,有足够的数据,队列可能会增长到耗尽所有内存。鉴于此,我正在尝试确定ThreadPoolExecutor的正确设置

我的第一个想法是使用直接切换和调用方运行失败策略的固定线程池。但我想知道这是否会影响吞吐量(因为每次调用调用方运行策略时,线程池任务都可能会完成并闲置一段时间)

另一个想法是使用ArrayBlockingQueue固定线程池,但我实际上不确定它的行为。我希望这意味着执行者更喜欢创建小于核心线程大小的线程,然后排队,如果队列已满,它会阻止等待队列获得空间。但是在阅读这里的文档时:

它似乎更喜欢创建corePoolSize以下的线程,然后添加到队列中,如果队列已满,它将尝试创建maxThreads以下的线程(与本例中的coreThreads相同),如果失败,它将运行失败策略


有人能澄清上述案例的行为吗?并建议针对这种特殊情况的最佳设置(我建议的一个想法或其他更好的想法)?

如果一个线程发出所有请求,那么一个无限队列将根据您的喜好进行阻塞,而不会产生队列增长的负面副作用


如果多个线程发出请求,则“带调用者运行的固定池”策略应按您的需要工作。池中的其余线程将由其他请求线程保持活动状态。

当当前使用所有线程时,ThreadPoolExecutor将创建更多线程。这意味着队列可以为空,但如果所有线程都在运行以前的任务,则新任务将创建一个新线程,直到达到最大值

如果队列已满且线程都已饱和,ThreadPoolExecutor将实际拒绝该任务并抛出一个
RejectedExecutionException
。因此,使用BlockingQueue实际上不会产生预期的结果

如果要限制队列中当前的任务数量,可以使用ExecutorCompletionService和支持队列

//core 5 max 10 with 60 second idle time
ThreadPoolExecutor executor = new ThreadPoolExecutor(5,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
ExecutorCompletionService completionService = new ExecutorCompletionService(executor);
private final static int MAX_IN_QUEUE = 1000; 

public void doSubmit(Runnable r){
    while(executor.getQueue().size() >= MAX_IN_QUEUE) 
       completionService.poll(100,TimeUnit.MILLISECONDS);
    completionService.submit(r);
}
//核心5最多10个,空闲时间为60秒
ThreadPoolExecutor executor=新的ThreadPoolExecutor(5,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue());
ExecutorCompletionService completionService=新的ExecutorCompletionService(executor);
队列中的私有最终静态int MAX_=1000;
公共无效数据提交(可运行r){
while(executor.getQueue().size()>=队列中的最大值)
poll(100,时间单位为毫秒);
完成服务。提交(r);
}
这有一个明显的副作用,就是必须持续等待一个元素完成。我在条件上循环,因为可能的种族条件,而实际上是真正的非物质性进入区块后


当然还有多个提交的竞争条件,但它应该足够节流以防止队列过度拥挤。这可以通过简单地同步
doSubmit
方法来解决。

只要队列长度过长,就可以让生产者暂停

类似这样的操作将等待任务队列的大小限制为最大值

ExecutorService service = 
Queue workQ = // queue of service.
BufferedReader br =
String line;
while((line = br.readline()) != null) {
    service.submit(new ProcessLineRunnable(line));
    while(workQ.size() > MAX_LEN) Thread.sleep(1);
}

我想我会给出另一个答案,因为这是同一个问题的不同解决方案

您可以只使用ThreadPoolExecutor和信号量。信号量将使用您希望在队列中允许的最大数量创建,并且在每个线程完成执行后,您将调用release(beforecute,即当项目从队列中拉出时)

信号量信号量=新信号量(1000);
ThreadPoolExecutor executor=新的ThreadPoolExecutor(5,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue()){
执行前受保护的void(可运行的r,可丢弃的t){
semaphore.release();
}
}
公共无效数据提交(可运行r){
sempahore.acquire();
执行人提交(r);
}

因此,在这里,所有线程都将挂起,直到有可用的许可证(队列上的条目)。

对于您的用例,我建议您测试它。什么是最好的取决于您想要维持的吞吐量,以及每个任务的延迟。线程池可以以每秒一百万个任务的速度轻松处理(前提是任务很简单)。如果生产者的速度太快,您是否可以选择减慢其速度?消费者将数据发布到Web服务,而生产者只是从文件中读取记录。所以希望你能明白为什么生产者快而消费者慢。如果我让生产者继续添加到队列中,我将耗尽内存。因此,我希望有一种方法可以很好地配置ThreadPoolExecutor来为我进行节流(在我的第一个示例中,称为RRunPolicy是一种粗略的节流形式)。理想情况下,当使用了最大线程且队列已满时,有一种方法可以使生产者块在队列中等待空间。每当队列长度超过某个长度时,我会让生产者暂停。(这是等待作业的长度)通过这种方式,您可以确保它不会变得太长。有一个生产者线程和N个消费者线程。在使用无界队列时,如何防止队列增长?你能详细说明一下吗?在这种情况下,我也应该规定调用方运行策略的使用。由于调用者(生产者)运行,并且只有一个调用者,因此当调用者忙于执行请求时,队列无法增长。但无界队列是否意味着从不调用失败策略?我同意呼叫方运行的直接切换或有界队列策略将起作用。我明白了。我想你是对的。我现在明白你的意思了。对于单个生产者线程,您必须在无限队列增长或潜在未充分利用的线程之间进行选择
Semaphore semaphore = new Semaphore(1000);
ThreadPoolExecutor executor = new ThreadPoolExecutor(5,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>()){
  protected void beforeExecute(Runnable r, Throwable t) { 
     semaphore.release();
  }
}

public void doSubmit(Runnable r){
  sempahore.acquire();
  executor.submit(r);      
}