在Java中使用锁的生产者和消费者

在Java中使用锁的生产者和消费者,java,multithreading,Java,Multithreading,我从中得到了这个例子。但在那里,他们只是一个接一个地实现了这个例子。平均第一个要产生的整数,然后混淆,程序停止 以下是原始示例: class ProducerConsumerImpl { // producer consumer problem data private static final int CAPACITY = 10; private final Queue queue = new LinkedList<>(); private fina

我从中得到了这个例子。但在那里,他们只是一个接一个地实现了这个例子。平均第一个要产生的整数,然后混淆,程序停止

以下是原始示例:

class ProducerConsumerImpl {
    // producer consumer problem data
    private static final int CAPACITY = 10;
    private final Queue queue = new LinkedList<>();
    private final Random theRandom = new Random();

    // lock and condition variables
    private final Lock aLock = new ReentrantLock();
    private final Condition bufferNotFull = aLock.newCondition();
    private final Condition bufferNotEmpty = aLock.newCondition();

    public void put() throws InterruptedException {
        aLock.lock();
        try {
            while (queue.size() == CAPACITY) {
                System.out.println(Thread.currentThread().getName()
                        + " : Buffer is full, waiting");
                bufferNotEmpty.await();
            }

            int number = theRandom.nextInt();
            boolean isAdded = queue.offer(number);
            if (isAdded) {
                System.out.printf("%s added %d into queue %n", Thread
                        .currentThread().getName(), number);

                // signal consumer thread that, buffer has element now
                System.out.println(Thread.currentThread().getName()
                        + " : Signalling that buffer is no more empty now");
                bufferNotFull.signalAll();
            }
        } finally {
            aLock.unlock();
        }
    }

    public void get() throws InterruptedException {
        aLock.lock();
        try {
            while (queue.size() == 0) {
                System.out.println(Thread.currentThread().getName()
                        + " : Buffer is empty, waiting");
                bufferNotFull.await();
            }

            Integer value = queue.poll();
            if (value != null) {
                System.out.printf("%s consumed %d from queue %n", Thread
                        .currentThread().getName(), value);

                // signal producer thread that, buffer may be empty now
                System.out.println(Thread.currentThread().getName()
                        + " : Signalling that buffer may be empty now");
                bufferNotEmpty.signalAll();
            }

        } finally {
            aLock.unlock();
        }
    }
}
已编辑 在考虑性能之后,我决定在
signalall()之前的
put
get
方法中添加if条件语句。(我认为它甚至可以提高0.000000左右的性能)但是中断wait();可能是一个
死锁()有什么帮助吗

对于生产者:

if(queue.size() == CAPACITY){
bufferNotFull.signalAll();
}
消费者:

if(queue.size() == 0){
bufferNotEmpty.signalAll();
}
修改后的代码就可以了

至于在不需要调用时跳过
.singnalll()
调用可能的性能优化:仅当条件从
false
更改为
true
时才发出信号就足够了

对于制作人,您可以使用:

if(isAdded && queue.size() == 1) {
    /*
     * Element has been *actually added* *into empty queue*
     * (previously .size() = 0), thus *queue become non-empty*.
     */
    bufferNotFull.signalAll();
}
if(value && queue.size() == CAPACITY - 1) {
    /*
     * Element has been *actually consumed* *from full queue*
     * (previousely .size() = CAPACITY), thus *queue become non full*.
     */
    bufferNotEmpty.signalAll();
}
对于消费者,您可以使用:

if(isAdded && queue.size() == 1) {
    /*
     * Element has been *actually added* *into empty queue*
     * (previously .size() = 0), thus *queue become non-empty*.
     */
    bufferNotFull.signalAll();
}
if(value && queue.size() == CAPACITY - 1) {
    /*
     * Element has been *actually consumed* *from full queue*
     * (previousely .size() = CAPACITY), thus *queue become non full*.
     */
    bufferNotEmpty.signalAll();
}

注意,这种方式并不能消除对服务员不必要的通知(例如,消费者只能为空队列等待元素,因此只有添加第一个元素才会唤醒它)。相反,当明确知道没有线程等待它时,您消除了对
.notifyAll()
的调用(例如,消费者不能在非空队列上等待)。

根据您的程序的输出,在我看来,它正在做它应该做的事情:生产者将东西放入队列,消费者将它们取出。如果将
while(true)
循环置于
try
/
finally
循环之外,可能会得到更多“混合”结果。然后,您可能会看到producer/producer/producer/consumer/consumer/consumer,而不是producer/producer/producer/consumer/consumer/consumer。正是由于Brian的评论,原始代码的行为比您的修改更自然。但原始代码对条件变量使用了混乱的命名:
bufferNotFull
用于检查队列是否为空,而
bufferNotEmpty
用于检查队列是否为空。请注意,在尝试减少信号发送时间时(在问题结束时),您使用变量名的自然意义:
bufferNotFull
queue.size()==CAPACITY
bufferNotEmpty
queue.size()==0
@Tsyvarev谢谢您的评论。好的,我明白了。没有concurreny/deadlock/hack/thread lock/thread safe问题吗?@BrianMalehorn谢谢,我刚刚决定先获得10个生产者,然后获得10个消费者,这就是为什么我使用while(true)inside try。那么concurreny/线程安全就没有问题了吗?@BrianMalehorn我将while循环放在try/finally之外,但它仍然生成相同的10乘10。。不是像你提到的那样一个接一个。在此处编码为什么使用
queue.size()==1
?这将只通知一次。在这种情况下,没有解锁。我确信这可能会导致死锁。任何使用者只能在空状态(大小为
0
)的队列中等待<每次队列从空状态(大小为
0
)转换为非空状态(大小为
1
)时,code>queue.size()==1
都会发出通知。这样我们就不会在需要的时候错过信号。我有点困惑,假设生产者会在
队列中通知。size()==1
之后锁不会释放。。。所以制作人会再次制作。。然后大小将达到1。。那么,产品在尺寸达到1后如何通知消费者呢?无需就每一个元素添加通知消费者,单次通知就足够了。收到通知后,消费者将等待
锁定
,直到生产者释放它(在元素插入完成系列之后)。然后使用者将发现队列不是空的,并将使用元素,直到队列再次变为空(
queue.size()
变为0)。依此类推。
消费者将自动开始消费并在不通知的情况下获得锁
-是。只有从
返回时才需要通知。等待()
。从
.lock()
(带锁)返回时不需要通知。