当我尝试学习Java编程的多线程部分时,在处理一个生产者-多个使用者编码时遇到以下问题。
我要实现的目标是:多个使用者线程按照将物品放入队列的顺序将它们从队列中取出。换句话说,使使用者线程总体上保持FIFO方式。
final BlockingDeque<String> deque = new LinkedBlockingDeque<String>();
Runnable rb = new Runnable() {
public void run() {
try {
System.out.println(deque.takeLast());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
deque.putFirst("a");
deque.putFirst("b");
deque.putFirst("c");
deque.putFirst("d");
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(rb);
pool.submit(rb);
pool.submit(rb);
pool.submit(rb);
我正在寻找什么: 一种 b C d
实际输出的内容: b C 一种 d
或以随机顺序
有什么简单的解决方案可以解决这个问题?谢谢!
答案 0 :(得分:1)
您的问题是
System.out.println(deque.takeLast());
实际上是两条指令,它们不是原子的。想象这样的情况:
所以这完全取决于操作系统将如何管理线程执行。
在您的情况下,一种可能的解决方案是将synchronized
关键字添加到run
方法中:
Runnable rb = new Runnable() {
public synchronized void run() {
try {
String s = deque.takeLast();
System.out.println(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
这将同步您在此处创建的匿名类的实例。由于您将相同的可运行对象传递给ExecutorService-它应该可以工作。
或者,您可以对queue
对象进行同步处理,因为将您传递给ExecutorService
的可运行对象(可以访问队列对象)将在许多线程中执行:
Runnable rb = new Runnable() {
public void run() {
synchronized (deque) {
try {
String s = deque.takeLast();
System.out.println(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
还请记住关闭线程池,因为现在您的应用程序将永远不会退出。