繁体   English   中英

ArrayBlockingQueue显示奇怪的行为

[英]ArrayBlockingQueue showing weired behavior

我正在研究BlockingQueue接口,其中ArrayBlockingQueue是一种实现。 出于演示目的,我开发了以下代码:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class MainJava {
    public static void main(String[] args) {
        BlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
        Producer producer = new Producer(queue);
        Consumer consumer = new Consumer(queue);
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

class Producer implements Runnable {
    BlockingQueue<String> queue = null;

    public Producer(BlockingQueue<String> queue) {
        this.queue = queue;
        // TODO Auto-generated constructor stub
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                System.out.println("Producer added " + i);
                queue.put(String.valueOf(i));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Consumer implements Runnable {
    BlockingQueue<String> queue = null;

    public Consumer(BlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                System.out.println("Consumer used " + queue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

预期行为

Producer add 0
Consumer used 0
Producer add 1
Consumer used 1 
and so on..

Actaul O / P

Producer added 0
Producer added 1
Consumer used 0
Consumer used 1
Producer added 2
Producer added 3
Producer added 4 

我尝试使用调试模式,在该模式下它可以正常工作。 那么,为什么没有调试模式,事情就无法正常工作?

您的代码按其应有的方式工作-这只是一种“错觉”,即您的队列似乎一次由于包含不正确的时间而在一个错误的地方包含一个System.out.println导致一次包含多个项目:

在你的生产者, System.out.println来阻断前put ,让你打印“制片人加X”即使put必须等待,直到队列变空。

但是,即使我们交换了这两行(将put System.out.println之前),输出似乎还是错误的:

Consumer used 0
Producer added 0
Producer added 1
Consumer used 1
Consumer used 2
Producer added 2
Producer added 3
Consumer used 3

尽管如此,这仍然是由于不幸的时机造成的错觉。 如您所见,在我的输出中,使用者似乎在生产者将其放入队列之前就消耗了元素0! 当然不是这样。 System.out是一个PrintStream对象,它是线程安全的。 因此,只有一个线程可以同时打印某些内容。 在上面的运行中,使用者线程只是在产生者之前抢了锁。

执行顺序可能是这样的:

  1. 生产者: put 0 into queue
  2. 消费者: take 0 from queue
  3. 消费者: print 'Consumer used 0'
  4. 消费者: wait until queue not empty
  5. 生产者: print 'Producer added 0'
  6. 生产者: put 1 into queue
  7. 生产者: print 'Producer added 1'
  8. 生产者: wait until queue not full
  9. 消费者: take 1 from queue
  10. 消费者: print 'Consumer used 1'
  11. 生产者: put 2 into queue
  12. 消费者: take 2 from queue
  13. 消费者: print 'Consumer used 2'
  14. 生产者: print 'Producer added 2'

等等

在多线程系统中,很难争论正确的行为...

@Kayaman实际上已经回答了这个问题,但是也许更多的信息可能会对您有所帮助。

编辑...和isnot2bad写了一个类似的答案(+1)。 我显然太慢了。 但是,也许它仍然被认为有用。

首先:代码

System.out.println("Producer added " + i);
queue.put(String.valueOf(i));

显然会在实际将其添加到队列之前打印Producer added 1 只需将线交换到

queue.put(String.valueOf(i));
System.out.println("Producer added " + i);

将减少输出看起来有误的情况的数量,但不会完全消除它们。 原因是这两行不是原子的

时间表示例显示了每个线程在每个步骤中所做的工作,它表明结果可能看起来是任意错误的,具体取决于操作的顺序:

Producer thread:          Consumer thread:           Queue:      Output:
------------------------------------------------------------------------
queue.put(0);                                        [0]
println("Added " + 0);                               []          Added 0
                          String 0 = queue.take();   []
                          println("Used  " + 0);     []          Used 0 
queue.put(1);                                        [1]         
                          String 1 = queue.take();   []
println("Added " + 1);                               []          Added 1
queue.put(2);                                        [2]         
println("Added " + 2);                               [2]         Added 2
                          println("Used  " + 1);     [2]         Used 1 
                          String 2 = queue.take();   []
                          println("Used  " + 2);     []          Used 2 
queue.put(3);                                        [3]         
                          String 3 = queue.take();   []
                          println("Used  " + 3);     []          Used 3 
println("Added " + 3);                               []          Added 3
queue.put(4);                                        [4]         
println("Added " + 4);                               [4]         Added 4

不过,关键是队列永远不会包含多个元素。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM