2016-09-27 160 views
2

我正在編寫一個使用ReentrantLock而不是​​生產者和消費者問題的小程序。但程序卡住了,因爲一旦生產的物品被消耗,消費者線程將停止並且不再恢復消耗。Java線程生產者和消費者

代碼片段:

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(); 
}} 
+0

我認爲你應該多調試一下,如果你能夠更具體地瞭解你所面臨的問題,那將會很棒。 –

+0

如何調試:https://ericlippert.com/2014/03/05/how-to-debug-small-programs/ –

+1

[BlockingQueue](https://docs.oracle.com/javase/8/docs/ api/java/util/concurrent/BlockingQueue.html)會讓你的生活更輕鬆。 –

回答

0

好了,你忘了做兩件事情:

  1. 更新變量n,當你消費和生產
  2. 從列表中刪除饅頭消耗後(這很難做清單,所以我推薦使用ArrayList代替)

此外,你的兩個條件是不必要的......你可以用一個做。這是工作代碼:

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(); 
      } 
     } 
    } 
} 
} 
0

我在寫一個小程序來使用,而不是ReentrantLock的生產者和消費者問題的同步。

也許這是一個學術問題,但如果不是,你應該肯定使用BlockingQueue爲您的解決方案。例如,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(...); 
} 

唯一的問題是,當你完成知道。通常人們通過向隊列添加一些常量「我們完成」對象來解決這個問題。

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

這真的很好!感謝您的BlockingQueue !!! –