我在java中有一个任务队列。此队列位于数据库的表中。
我需要:
我想我可以这样做:
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的查询非常繁重,我不希望同时拥有不受控制的任务数。
答案 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)
实现良好的性能还取决于线程中需要完成的工作类型。如果您的数据库是处理的瓶颈,我会开始关注您的线程如何访问数据库。使用连接池可能是有序的。这可能有助于您获得更多吞吐量,因为工作线程可以重用池中的数据库连接。