多个使用者线程消耗整个队列FIFO

时间:2019-06-09 19:22:26

标签: java multithreading fifo

当我尝试学习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

或以随机顺序

有什么简单的解决方案可以解决这个问题?谢谢!

1 个答案:

答案 0 :(得分:1)

您的问题是

System.out.println(deque.takeLast());

实际上是两条指令,它们不是原子的。想象这样的情况:

  1. 线程1从队列中获取字符串。
  2. 线程2从队列中获取字符串。
  3. 线程2打印出值。
  4. 线程1输出值。

所以这完全取决于操作系统将如何管理线程执行。

在您的情况下,一种可能的解决方案是将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();
             }
        }
    }
};

还请记住关闭线程池,因为现在您的应用程序将永远不会退出。