简体   繁体   English

Java线程生产者和使用者

[英]Java thread producer and consumer

I was writing a small program to use ReentrantLock instead of synchronized of producer and consumer problem. 我正在编写一个小程序来使用ReentrantLock而不是synchronized生产者和使用者问题。 But the program got stuck because once the produced items are consumed, the consumer thread will stop and never resume to consume again. 但是程序卡住了,因为一旦消费了生产的商品,消费线程将停止并且永远不会恢复消费。

Code snippet: 程式码片段:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Mantou2 {
int id;

public Mantou2(int id) {
    this.id = id;
}}

class Basket2 {
final int max = 20;
Mantou2[] ms;
int n;
Lock lock;
Condition full;
Condition empty;

public Basket2() {
    ms = new Mantou2[max];
    n = 0;

    lock = new ReentrantLock();
    full = lock.newCondition();
    empty = lock.newCondition();
}

public void consume() {
    lock.lock();

    try {
        while (n == 0) {
            System.out.println("No Mantou left!");
            empty.await();
        }

        empty.signal();
        System.out.println(ms[--n].id + " consumed and " + n + " left");
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } finally {
        lock.unlock();
    }

}

public void produce() {
    lock.lock();
    try {
        while (n == max) {
            System.out.println("Inventory is full!");
            full.await();
        }

        full.signal();
        ms[n] = new Mantou2(n++);
        System.out.println(ms[n - 1].id + " produced and " + n + " left");
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } finally {
        lock.unlock();
    }
}}

class Consumer2 implements Runnable {
Basket2 basket;

public Consumer2(Basket2 basket) {
    this.basket = basket;
}

@Override
public void run() {
    // TODO Auto-generated method stub
    for (int i = 0; i < 50; i++) {
        basket.consume();
        try {
            Thread.sleep((long) (Math.random() * 300));
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}}

class Producer2 implements Runnable {
Basket2 basket;

public Producer2(Basket2 basket) {
    this.basket = basket;
}

@Override
public void run() {
    // TODO Auto-generated method stub
    for (int i = 0; i < 50; i++) {
        basket.produce();
        try {
            Thread.sleep((long) (Math.random() * 300));
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}}

public class ProducerCustomer2 {

public static void main(String[] args) {
    // TODO Auto-generated method stub
    Basket2 basket = new Basket2();
    Producer2 producer = new Producer2(basket);
    Consumer2 consumer = new Consumer2(basket);
    Thread p = new Thread(producer);
    Thread c = new Thread(consumer);
    p.start();
    c.start();
}}

Okay you forgot to do two things: 好的,您忘了做两件事:

  1. update the variable n when you consume and produce 消费和生产时更新变量n
  2. remove the Mantou from the list after consume (this is hard to do with a list and so I recommend using ArrayList instead) 用完后从列表中删除Mantou(这很难用列表完成,因此我建议改用ArrayList)

Also, your two conditions are unnecessary... You can just do it with one. 另外,您不需要两个条件……您可以只用一个条件。 This is the working code: 这是工作代码:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.ArrayList;

public class ProducerCustomer2 {

public static void main(String[] args) {
    // TODO Auto-generated method stub
    ProducerCustomer2 pc = new ProducerCustomer2();
    pc.start();
}

public void start() {
    Basket2 basket = new Basket2();
    Producer2 producer = new Producer2(basket);
    Consumer2 consumer = new Consumer2(basket);
    Thread p = new Thread(producer);
    Thread c = new Thread(consumer);
    p.start();
    c.start();
}

private class Mantou2 {
    int id;

    public Mantou2(int id) {
        this.id = id;
    }
}

private class Basket2 {
    final int max = 20;
    ArrayList<Mantou2> ms;
    int n;
    Lock lock;
    Condition full;
    Condition empty;

    public Basket2() {
        ms = new ArrayList<Mantou2>();
        n = 0;

        lock = new ReentrantLock();
        full = lock.newCondition();
        empty = lock.newCondition();
    }

    public void consume() {
        lock.lock();

        try {
            while (n == 0) {
                System.out.println("No Mantou left!");
                full.await();
            }
            System.out.println(ms.get(n-1).id + " consumed and " + n + " left");
            ms.remove(n-1);
            n--;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            full.signal();
            lock.unlock();
        }

    }

    public void produce() {
        lock.lock();
        try {
            while (n == max) {
                System.out.println("Inventory is full!");
                full.await();
            }
            n++;
            ms.add(new Mantou2(n));
            System.out.println(ms.get(n-1).id + " produced and " + n + " left");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            full.signal();
            lock.unlock();
        }
    }
}

private class Consumer2 implements Runnable {
    Basket2 basket;

    public Consumer2(Basket2 basket) {
        this.basket = basket;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 50; i++) {
            basket.consume();
            try {
                Thread.sleep((long) (Math.random() * 300));
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

private class Producer2 implements Runnable {
    Basket2 basket;

    public Producer2(Basket2 basket) {
        this.basket = basket;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 50; i++) {
            basket.produce();
            try {
                Thread.sleep((long) (Math.random() * 300));
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}
}

I was writing a small program to use ReentrantLock instead of synchronized of producer and consumer problem. 我正在编写一个小程序来使用ReentrantLock,而不是同步生产者和使用者问题。

Maybe this is an academic question but if not, you should definitely use BlockingQueue for your solution. 也许这是一个学术问题,但如果不是,您绝对应该使用BlockingQueue作为解决方案。 For example the LinkedBlockingQueue takes care of all of the synchronization, counting, complexity, etc.. Your code would then look like: 例如, LinkedBlockingQueue负责所有同步,计数,复杂性等。代码如下所示:

BlockingQueue<Mantou2> queue = new LinkedBlockingQueue<>(20);

// producer
for (int i = 0; i < 50; i++) {
   queue.add(new Mantou2(i));
   Thread.sleep(...);
}

// consumer
for (int i = 0; i < 50; i++) {
   Mantou2 mantou2 = queue.take();
   System.out.println(mantou2.id + " consumed and " + queue.size() + " left");
   Thread.sleep(...);
}

The only trick is do know when you are done. 唯一的技巧是知道何时完成。 Typically people solve this by adding some constant "we are done" object to the queue. 通常,人们通过在队列中添加一些恒定的“我们已完成”对象来解决此问题。

 private final Mantou2 WE_ARE_DONE = new Mantou2();
 ...

 // producer says that we are done
 queue.add(WE_ARE_DONE);
 ...

 // consumer
 Mantou2 mantou2 = queue.take();
 if (mantou2 == WE_ARE_DONE) {
    break;
 }
 ...

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

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