【问题标题】:Java concurrency: Modifying latch/ThreadGroup to achieve Executor behaviourJava并发:修改latch/ThreadGroup实现Executor行为
【发布时间】:2017-09-16 01:16:53
【问题描述】:

这个问题与我在 Java 并发主题中的作业有关。我的任务是生成新线程并通过给定的concurrencyFactor 限制它们。也就是说,继续调度新线程,直到活动线程数小于或等于concurrencyFactor。如果活动线程数等于concurrencyFactor,程序将等待活动线程数减少到concurrencyFactor - 1并创建一个新线程。

作为第一种方法,我使用ExecutorService 并通过Executors.newFixedThreadPool(concurrencyFactor); 创建了一个新的固定池,每当调用我的方法时,我只是向该池提交一个新的可运行文件。逻辑代码如下:

    private final ExecutorService fixedPoolExecutor = Executors.newFixedThreadPool(concurrencyFactor);
    public void handleRequest(final RequestHandler handler) {
    if (handler == null) throw new IllegalArgumentException("Handler cannot be null");
    fixedPoolExecutor.submit(new Runnable() {
        @Override
        public void run() {
            handler.serviceRoutine();
        }
      });
    }

现在,第二部分要求我实现相同的目标,但不使用执行器。我想到了以下两种方法:
1) 使用countDownLatch,但这个锁会等待(即latch.await())直到activeCount变成0。我只想等到倒计时变成concurrencyFactor - 1。
2) 使用ThreadGroup 并等到threadGroup.activeCount() < concurrencyFactor。但是,这种方法的问题是如何让传入的请求等到条件threadGroup.activeCount() < concurrencyFactor 满足?我为这种方法使用了以下代码:

    private final Lock lock = new ReentrantLock();
    private final ThreadGroup threadGroup = new ThreadGroup("myGroup");
    public void handleRequest(final RequestHandler handler) {
    if (handler == null) throw new IllegalArgumentException("Handler cannot be null");
    lock.lock();
    try {
        while (threadGroup.activeCount() >= concurrencyFactor) {

        }
        Thread t = new Thread(threadGroup, new Runnable() {
            @Override
            public void run() {
                handler.service();
            }
        });
        t.start();
    } finally {
        lock.unlock();
    }        
   }

我可以在第二种方法中用一些等待条件替换空白的while循环吗?

任何关于上述方法的建议或任何新方法的建议都将不胜感激。

【问题讨论】:

  • 作业是否强制您使用 CountDownLatch?
  • 不,它不强制。但是说明提示使用锁存器/线程/执行器来实现这一点。
  • 因为这似乎非常适合 Semaphore。
  • 问题是:什么时候可以调用semaphore.release()?我必须知道当前活动线程的数量。
  • 看我的回答:)

标签: java multithreading concurrency executorservice countdownlatch


【解决方案1】:

我建议使用Sempahore。信号量将表示仍允许启动的线程数。最初它持有等于配置的并发因子的许可。

在开始一个新线程之前,handleRequest 方法需要从信号量中获取一个许可。启动的线程应该在完成后再次允许释放。

示例代码:

private final ThreadGroup threadGroup = new ThreadGroup("myGroup");
private final Semaphore concurrencyFactor = new Semaphore(CONCURRENCY_FACTOR);

public void handleRequest(final RequestHandler handler) throws InterruptedException {
    if (handler == null) throw new IllegalArgumentException("Handler cannot be null");

    concurrencyFactor.acquire(); // Get permit

    Thread t = new Thread(threadGroup, new Runnable() {
        @Override
        public void run() {
            try {
                handler.service();
            } finally {
                concurrencyFactor.release(); // make sure to release permit
            }
        }
    });
    t.start();
}

(您可能希望以不同的方式处理可能的中断)

【讨论】:

  • run方法中的try块是否保证concurrencyFactor只有在service方法运行完成后才释放?
  • 它确保即使在 service() 中抛出任何异常,也能释放许可证
  • 如果我们使用信号量,那为什么要使用threadGroup呢?这不会导致不必要的组管理开销吗?
  • ThreadGroup 无助于解决问题。我保留它是因为我认为你出于某种原因想要它。
  • 太棒了!非常感谢您的帮助。
猜你喜欢
  • 2017-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多