等待空 BlockingQueue 的首选方法
Preferable approach for waiting on empty BlockingQueue
我有一个多线程应用程序,其中一个线程将项目放入 BlockingQueue
中,多个线程从中取出项目进行处理。问题是关于从队列中取出物品,目前是这样实现的:
class PriorityQueueWorker<T> implements Runnable {
private final PriorityBlockingQueue<T> requestQueue;
private final AtomicBoolean continueExecution;
@Override
public void run() {
do {
T request = null;
try {
request = requestQueue.take();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
continueExecution.set(false);
}
if (request == null) {
continue;
}
//... here we do the work
} while (continueExecution.get());
}
根据 BlockingQueue.take()
的 JavaDoc,它 检索并删除此队列的头部,必要时等待直到元素可用 和 PriorityBlockingQueue
这意味着线程将在 PriorityBlockingQueue.take()
上被阻塞,直到一个项目出现在队列中:
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
E result;
try {
while ((result = dequeue()) == null)
notEmpty.await();
} finally {
lock.unlock();
}
return result;
}
另一种实现我们逻辑的方法是使用 PriorityBlockingQueue.poll()
which returns null
在空队列的情况下:
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return dequeue();
} finally {
lock.unlock();
}
}
据我了解,这些方法的区别在于 take()
线程被阻塞在空队列等待中,而 poll()
它们在 poll()
中继续围绕无限循环旋转=21=].
对我来说,队列长时间为空的用例很常见,所以我的问题是从性能的角度来看哪种方法更好:保持线程阻塞(take-approach)或保持它们旋转(轮询方法)?
which approach is better as of performance point of view...?
“性能”对您意味着什么?
轮询空队列的工作人员将尝试使用 100% 的 CPU 核心。这将限制该核心代表任何其他线程工作的可用性。它还消耗功率,并产生热量。但是,如果您的应用程序中的线程数与 CPU 内核的数量仔细匹配,那么它可以使您的工作线程更快地响应新的工作项:当工作项到达时,很有可能一些工作人员已经在核心 运行ning 并且能够立即处理它。
在许多(大多数?)应用程序中,OTOH,应用程序中的其他线程和其他进程 运行ning 都希望 share CPU 个核心。在这种情况下,使用 take()
可以释放 CPU 核心,以便在您的员工无事可做时做其他事情。那也是一种“表演”。另外,使用 take()
意味着您的应用程序将消耗更少的电量(如果您的应用程序可以在电池供电的设备上 运行 就很重要),并且它会产生更少的热量(如果您的应用程序可能是个问题运行 正在数据中心或高温环境中。)take()
的缺点是,当新工作项到达时,OS 需要一些时间才能完成“唤醒”等待的工作线程,以便它可以开始处理它。
IMO:您应该从 take()
开始,并且只有在 确定 时才尝试轮询(即,如果您 已测量 性能)您有问题,并且您确定(再次测量)轮询修复了它。另外,除非您可以保证执行轮询的线程数少于可用内核数,否则不要尝试轮询。
我有一个多线程应用程序,其中一个线程将项目放入 BlockingQueue
中,多个线程从中取出项目进行处理。问题是关于从队列中取出物品,目前是这样实现的:
class PriorityQueueWorker<T> implements Runnable {
private final PriorityBlockingQueue<T> requestQueue;
private final AtomicBoolean continueExecution;
@Override
public void run() {
do {
T request = null;
try {
request = requestQueue.take();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
continueExecution.set(false);
}
if (request == null) {
continue;
}
//... here we do the work
} while (continueExecution.get());
}
根据 BlockingQueue.take()
的 JavaDoc,它 检索并删除此队列的头部,必要时等待直到元素可用 和 PriorityBlockingQueue
这意味着线程将在 PriorityBlockingQueue.take()
上被阻塞,直到一个项目出现在队列中:
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
E result;
try {
while ((result = dequeue()) == null)
notEmpty.await();
} finally {
lock.unlock();
}
return result;
}
另一种实现我们逻辑的方法是使用 PriorityBlockingQueue.poll()
which returns null
在空队列的情况下:
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return dequeue();
} finally {
lock.unlock();
}
}
据我了解,这些方法的区别在于 take()
线程被阻塞在空队列等待中,而 poll()
它们在 poll()
中继续围绕无限循环旋转=21=].
对我来说,队列长时间为空的用例很常见,所以我的问题是从性能的角度来看哪种方法更好:保持线程阻塞(take-approach)或保持它们旋转(轮询方法)?
which approach is better as of performance point of view...?
“性能”对您意味着什么?
轮询空队列的工作人员将尝试使用 100% 的 CPU 核心。这将限制该核心代表任何其他线程工作的可用性。它还消耗功率,并产生热量。但是,如果您的应用程序中的线程数与 CPU 内核的数量仔细匹配,那么它可以使您的工作线程更快地响应新的工作项:当工作项到达时,很有可能一些工作人员已经在核心 运行ning 并且能够立即处理它。
在许多(大多数?)应用程序中,OTOH,应用程序中的其他线程和其他进程 运行ning 都希望 share CPU 个核心。在这种情况下,使用 take()
可以释放 CPU 核心,以便在您的员工无事可做时做其他事情。那也是一种“表演”。另外,使用 take()
意味着您的应用程序将消耗更少的电量(如果您的应用程序可以在电池供电的设备上 运行 就很重要),并且它会产生更少的热量(如果您的应用程序可能是个问题运行 正在数据中心或高温环境中。)take()
的缺点是,当新工作项到达时,OS 需要一些时间才能完成“唤醒”等待的工作线程,以便它可以开始处理它。
IMO:您应该从 take()
开始,并且只有在 确定 时才尝试轮询(即,如果您 已测量 性能)您有问题,并且您确定(再次测量)轮询修复了它。另外,除非您可以保证执行轮询的线程数少于可用内核数,否则不要尝试轮询。