java - 从队列中取出的正确方法?
问题描述
下面的代码随机冻结。
队列在开始时是预先填充的,并且仅在线程开始从中获取项目之后才被获取。
我想我没有正确使用队列。尽管进行了isEmpty()
检查,但当一个线程尝试获取一项时,队列可能为空,使其无限期等待。
@Override
public void run() {
long milisecs;
try {
while ( ! queue.isEmpty()) { // !!!
milisecs = queue.take(); // !!!
worker(milisecs);
}
} catch (InterruptedException ex) {}
}
例如,如果发生这种情况,它会挂起:
- threadA 检查是否
queue.isEmpty()
,得到一个 false 并尝试继续。 - threadB
take()
队列中的最后一项 - threadA 尝试
take()
从空队列中获取项目,使其挂起。
应该同步“如果队列不为空则取”的过程,以使队列之间不会发生变化。
这样做的正确方法是什么?
完整代码如下。每次运行大约需要 1 秒。
package multithreadperformance;
import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadLocalRandom;
public class MultithreadPerformance implements Runnable {
static int numThreads = 50;
static int numJobs = 5000;
final BlockingQueue<Long> queue = new LinkedBlockingQueue<>();;
static ArrayList<Thread> threads;
public static void main(String[] args) {
MultithreadPerformance bench = new MultithreadPerformance();
bench.go();
}
public void go() {
System.out.print("Go... ");
long t0 = System.nanoTime();
// Fill up the queue of jobs with a random number of miliseconds.
long milisecs, milisecsMax = 20; // ms
//
try {
for (int i = 0; i < numJobs; i++) {
milisecs = ThreadLocalRandom.current().nextLong(milisecsMax);
queue.put(milisecs);
}
} catch (InterruptedException ex) {
System.out.println(ex.toString());
}
// Create all threads
threads = new ArrayList<>();
for(int i = 0; i < numThreads; i++) {
Thread thread = new Thread(this);
thread.setName("Thread" + i);
threads.add(thread);
}
// Start all threads
threads.forEach((thread) -> {thread.start();});
// Join all threads
threads.forEach((thread) -> {try {
thread.join();
} catch (InterruptedException ex) {
System.out.println(ex.toString());
}
});
long et = System.nanoTime() - t0;
System.out.println(String.format("done. Elapsed time %.3f s.", et/1e9));
}
// Worker function
// Sleep a number of miliseconds.
public void worker(long milisecs) throws InterruptedException {
Thread.sleep(milisecs);
}
@Override
public void run() {
long milisecs;
try {
while ( ! queue.isEmpty()) {
milisecs = queue.take();
worker(milisecs);
}
} catch (InterruptedException ex) {
System.out.println(ex.toString());
}
}
}
解决方案
您可以调用poll()
which 将自动删除队列的头部,或者null
如果队列为空则返回。
Long millisecs;
while ( (millisecs = queue.poll()) != null) {
worker(millisecs);
}
推荐阅读
- macos - 使用 CLion 在 Mac 上调试 xv6
- python - Pandas datetime 查找给定日期之前最近的日期。如果不存在,则获取最近的日期
- javascript - 随机给了我 2 个字符串不是一个
- android - 图像未在android模拟器中保存到磁盘
- java - 使用 spring ReflectionUtils 从 HttpComponentsClientHttpRequestFactory 检索超时值
- html - 如何向此 CSS 'Switcher' 'Toggle Switch' 添加文本
- r - ggplot2:两个不同的多重GLM泊松模型回归调整和置信区间
- c++ - C++ 头文件和声明嵌套结构和类语法
- jquery - 在计算机上触发按键
- html - 如何使用 CSS 修改这些按钮以获得不同的颜色/文本颜色/等?