在Java ThreadPoolExecutor中运行时更改线程池大小

时间:2020-09-25 17:23:59

标签: java multithreading threadpoolexecutor countdownlatch

我是Java核心编程的新手,目前正在从事与多线程相关的项目。

我们有多个客户调用的异步服务。我们为每个客户端有一个单独的使用者,它使用为客户端分配的固定数量的线程来消费和处理队列中的请求。现在的问题是,有些客户在一周内发送大量流量,而有些则在周末发送。 例如客户1在工作日每秒发送900个请求,而在周末仅发送200个请求。另一方面,客户2将在工作日发送100次,但在周末发送1000次。

因此,我们需要实时更新每个客户端的线程分配,而无需重新启动服务。根据我的理解,setCorePoolSize自动覆盖池大小并停止空闲线程。但是在我们的设置中,我们确保在开始执行当前任务之前始终将下一个任务提交给线程。因此线程永远不会变得空闲,并且减小池大小可能是个问题。

我看到了几篇关于执行之前或之后中断线程的文章,我尝试使用CountDownLatch编写代码段:

DynamicThreadPool类:

public class DynamicThreadPool extends ThreadPoolExecutor {
    private int currentThreadCount;
    private AtomicReference<CountDownLatch> executeLatch;

    public DynamicThreadPool(int nThreads) {
        super(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
        currentThreadCount = nThreads;
        executeLatch = new AtomicReference<>(new CountDownLatch(0));
    }

    @Override
    public void beforeExecute(Thread t, Runnable r)
    {
        super.beforeExecute(t, r);
        try {
            executeLatch.get().await();
        } catch (final InterruptedException e) {
            t.interrupt();
        }
    }

    public void resize(int nThreads) {
        if (currentThreadCount == nThreads) {
            return;
        }

        try {
            executeLatch.getAndSet(new CountDownLatch(1));
            currentThreadCount = nThreads;
            setCorePoolSize(nThreads);
            setMaximumPoolSize(nThreads);
            prestartAllCoreThreads();
            executeLatch.get().countDown();
        }
        catch (Exception e) {
            log.warn("resizer caught exception");
        }
    }
}

消费类:

public class Consumer {
   int numberOfProcessingThreads;

   public Consumer(int nThreads) {
     numberOfProcessingThreads = n Threads;
     DynamicThreadPool threadPool = new DynamicThreadPool(nThreads);
   } 

    public void start() {
       for (int  i = 0; i < messageProcessingThreads; i++) 
       {
          threadPool.submit(new MessageConsumer.ProcessRequest());
       }        
    }

    public void updateNumberOfThreads(int newThreadCount) {
      threadPool.resize(newThreadCount);
    }

   class ProcessRequest implements Runnable { 
       // 1. Logic to submit a new ProcessRequestTask if consumer is consuming messages
       threadPool.submit(new MessageConsumer.ProcessRequest());
       // 2. Logic to process request   
   }  
}

总而言之,当调用resize函数时,CountDownLatch设置为1,以便所有线程都在executeLatch.get()。await()上被阻塞。然后更改池大小,启动所有核心线程,并使用countDown函数取消阻止等待的线程。 假设每当需要更新线程号时都会调用updateNumberOfThreads,这种方法有意义吗?

我担心的是,如果某些线程没有因为正在执行而在executeLatch.get()。await()上被阻塞,则这些线程将永远不会变得空闲并且永远不会停止运行。 (例如,如果我们将池大小从20减少到10,但是beforeExecute方法中只有5个线程被阻塞,那么剩下的15个线程将​​永远不会变为空闲状态并继续运行)

是否有更好的方法来实现这一目标?如果我没有在执行之前向线程提交新任务,如何确保线程不处于空闲状态并且始终在处理请求?任何帮助表示赞赏。

0 个答案:

没有答案
相关问题