简体   繁体   中英

Java thread producer and consumer

I was writing a small program to use ReentrantLock instead of synchronized of producer and consumer problem. 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
  2. remove the Mantou from the list after consume (this is hard to do with a list and so I recommend using ArrayList instead)

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.

Maybe this is an academic question but if not, you should definitely use BlockingQueue for your solution. For example the LinkedBlockingQueue takes care of all of the synchronization, counting, complexity, etc.. Your code would then look like:

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;
 }
 ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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