如何管理M个线程(每个任务1个),同时确保只有N个线程。 N < M.在Java中

时间:2009-09-12 18:55:23

标签: java concurrency multithreading

我在java中有一个任务队列。此队列位于数据库的表中。

我需要:

  • 每个任务只有1个帖子
  • 不超过N个线程同时运行。这是因为线程具有数据库交互,我不希望打开一堆数据库连接。

我想我可以这样做:

final Semaphore semaphore = new Semaphore(N);
while (isOnJob) {
    List<JobTask> tasks = getJobTasks();
    if (!tasks.isEmpty()) {
        final CountDownLatch cdl = new CountDownLatch(tasks.size());
        for (final JobTask task : tasks) {
            Thread tr = new Thread(new Runnable() {

                @Override
                public void run() {
                    semaphore.acquire();
                    task.doWork();
                    semaphore.release();
                    cdl.countDown();
                }

            });
        }
        cdl.await();
    }
}

我知道ExecutorService类存在,但我不确定是否可以使用它。

那么,你认为这是最好的方法吗?或者你能否澄清一下ExecutorService如何解决这个问题?

最终解决方案:

我认为最好的解决方案是:

while (isOnJob) {
    ExecutorService executor = Executors.newFixedThreadPool(N);
    List<JobTask> tasks = getJobTasks();
    if (!tasks.isEmpty()) {
        for (final JobTask task : tasks) {
            executor.submit(new Runnable() {

                @Override
                public void run() {
                    task.doWork();
                }

            });
        }
    }
    executor.shutdown();
    executor.awaitTermination(Long.MAX_VALUE, TimeUnit.HOURS);
}

非常感谢这些人。 BTW我使用的是连接池,但是对DB的查询非常繁重,我不希望同时拥有不受控制的任务数。

4 个答案:

答案 0 :(得分:7)

您确实可以使用ExecutorService。例如,使用newFixedThreadPool方法创建新的固定线程池。这样,除了缓存线程之外,还可以保证不会有超过n个线程同时运行。

这些方面的东西:

private static final ExecutorService executor = Executors.newFixedThreadPool(N);
// ...
while (isOnJob) {
    List<JobTask> tasks = getJobTasks();
    if (!tasks.isEmpty()) {
        List<Future<?>> futures = new ArrayList<Future<?>>();
        for (final JobTask task : tasks) {
                Future<?> future = executor.submit(new Runnable() {    
                        @Override
                        public void run() {
                                task.doWork();
                        }
                });
                futures.add(future);
        }
        // you no longer need to use await
        for (Future<?> fut : futures) {
          fut.get();
        }
    }
}

请注意,您不再需要使用锁存器,因为get将等待计算完成,如有必要。

答案 1 :(得分:4)

我同意JG的观点ExecutorService是要走的路......但我认为你们都让它变得更加复杂。

为什么不创建一个固定大小的线程池(使用Executors.newFixedThreadPool(N))并将所有任务提交给它,而不是创建大量线程(每个任务1个)?不需要信号量或类似的东西 - 只需在获取它们时将作业提交到线程池,并且线程池将一次处理最多 N个线程。

如果您一次不打算使用超过N个线程,为什么要创建它们呢?

答案 2 :(得分:1)

ThreadPoolExecutor 实例与未绑定队列和固定的最大线程大小一起使用,例如: Executors.newFixedThreadPool(N)。这将接受大量任务,但只会同时执行 N

如果您选择有界队列(容量为 N ),执行者将拒绝执行任务(具体取决于您可以配置的策略)直接使用 ThreadPoolExecutor 时,而不是使用 Executors 工厂 - 请参阅 RejectedExecutionHandler )。

如果您需要“真正的”拥塞控制,则应设置容量为 N 的绑定 BlockingQueue 。从数据库中获取您想要完成的任务,并它们放入队列中 - 如果它已满,则调用线程将阻塞。在另一个线程中(可能也开始使用 Executor API),您 BlockingQueue 中获取任务,并将它们提交给 Executor 。如果BlockingQueue为空,则调用线程也将阻塞。要表示您已完成使用“特殊”对象(例如标记队列中最后/最后项目的单例)。

答案 3 :(得分:0)

实现良好的性能还取决于线程中需要完成的工作类型。如果您的数据库是处理的瓶颈,我会开始关注您的线程如何访问数据库。使用连接池可能是有序的。这可能有助于您获得更多吞吐量,因为工作线程可以重用池中的数据库连接。