Java中使用锁的生产者/消费者线程示例

Posted

技术标签:

【中文标题】Java中使用锁的生产者/消费者线程示例【英文标题】:Producer/Consumer thread example in Java using lock 【发布时间】:2021-10-06 10:27:34 【问题描述】:
public class ConsumerThreadExample 
public static void main(String[] args) throws InterruptedException 
    Consumer c = new Consumer();
    Thread a = new Thread(()->while(true)c.consume(););
    Thread b = new Thread(()->while(true)c.supply(););

    a.start();
    b.start();

    a.join();
    b.join();
    System.out.println("finish");



class Consumer
private List<Integer> buffer = new ArrayList<>();
private Lock lock = new ReentrantLock();
private Condition notEnough = lock.newCondition();
private Condition toomuch = lock.newCondition();

public void consume()
    lock.lock();
    try
        while(buffer.isEmpty())notEnough.await();
            System.out.println("consume waiting");
        System.out.println(buffer);

        for(int i = 0; i < buffer.size(); i++)
            System.out.println("consume "+ buffer.remove(i));

            try 
                sleep(3000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
        
        System.out.println("signal supply");
        toomuch.signal();
    
    catch (Exception e)
    finally 
        lock.unlock();
    


public void supply()
    lock.lock();
    try
        while(!buffer.isEmpty())toomuch.await();
            System.out.println("supply waiting");
        System.out.println("Adding");
        buffer.add(1);
        buffer.add(3);
        buffer.add(5);
        System.out.println(buffer);
        System.out.println("signal consume");
        notEnough.signal();
    
    catch (Exception e)
    finally 
        lock.unlock();
    


大家好,考虑上面的代码,我想练习一个经典的线程示例,即消费者/生产者。所以我希望代码用供应()填充缓冲区, 然后向consume()发出信号以消耗缓冲区。每当缓冲区为空时,consume()再次向supply()发出信号,依此类推。但输出似乎有点奇怪。输出:

Adding
[1, 3, 5]
signal consume
consume waiting
[1, 3, 5]
consume 1
consume 5
signal supply
[3]
consume 3
signal supply
supply waiting
Adding
[1, 3, 5]
signal consume
consume waiting
[1, 3, 5]
consume 1
consume 5
signal supply
[3]
consume 3
signal supply
supply waiting
Adding
[1, 3, 5]

为什么它消耗 1 和 5 然后信号 supply()? 3在哪里?为什么它不是1,3,5的顺序?

【问题讨论】:

另外,在消费者使用 remove(0) 和循环 while !isEmpty()。答案解释了当前的故障。 【参考方案1】:

在第一次迭代中

i = 0
buffer = [1, 3, 5]

然后你删除 1. 在第二次迭代中

i = 1
buffer = [3, 5]

然后删除 5。然后退出循环,因为 i 现在大于长度。

【讨论】:

哦,这很清楚,谢谢。那么,当您同时删除列表中的内容时,是否建议使用迭代器遍历列表?我试过用forEach,输出看起来也很奇怪。 您可以使用迭代器或 foreach 循环并在之后清除列表,或者退出条件应为空列表并始终删除第一个元素。当我想像这样连接 2 个线程时,我也会使用 BlockingQueue

以上是关于Java中使用锁的生产者/消费者线程示例的主要内容,如果未能解决你的问题,请参考以下文章

JAVA基础再回首(二十五)——Lock锁的使用死锁问题多线程生产者和消费者线程池匿名内部类使用多线程定时器面试题

双缓冲队列-减少生产者消费者锁的调用

Java并发多线程编程——生产者消费者模式示例(传统版本)

Java并发多线程编程——生产者消费者模式示例(阻塞队列版本)

java多线程--“升级版”生产者消费者

(二)线程状态、wait/notify