Java多线程:慢速生产者和快速消费者

时间:2017-04-14 04:08:20

标签: java multithreading producer-consumer

我试图了解同步块。在这里,我实现了一个生产者线程和2个消费者线程。

由于LinkedList为空,我一直在线程中获得异常。

package com.Main;

import com.qed.Consumer;
import com.qed.Producer;
import com.qed.Store;

public class Main {

public static void main(String[] args) throws InterruptedException {
    Store st = new Store();

    Thread populate = new Thread(new Producer(st)); 
    Thread con1 = new Thread(new Consumer(st));
    Thread con2 = new Thread(new Consumer(st));
    con1.setName("A");
    con2.setName("B");
    populate.start();
    con1.start();
    con2.start();

    populate.join();
    con1.join();
    con2.join();
    if(populate.isAlive()){
        con1.interrupt();
        con2.interrupt();
    }
  }
}

package com.qed;

import java.util.LinkedList;

public class Store {

private LinkedList<Integer> qu = new LinkedList<Integer>();
private final Object lock = new Object();

public void add(int data){
    try{
        while(qu.size() ==10){
            Thread.sleep(1);
            }
        qu.add(data);
    }catch(InterruptedException ie){
        ie.printStackTrace();
    }
}

public int remove(){
    int data=0;
    try{
        synchronized(lock){
            while(qu.size() == 0){
                Thread.sleep(1);
                }
            data = qu.removeFirst();
         }
    }catch(InterruptedException ie){
        ie.printStackTrace();
    }
    return data;
  }
}

package com.qed;

public class Consumer implements Runnable{

private Store st;
public Consumer(Store st){
    this.st=st;
}


public void run(){
    while(true){
        System.out.println(Thread.currentThread().getName() + ". " +st.remove());
    }
  }
}

package com.qed;

public class Producer implements Runnable{

private Store st;
private final int runs = 5000;
public Producer(Store st){
    this.st = st;
}

public void run(){
    int data = 0;
    int curRun =0;
    while(++curRun < runs){
        st.add(data+=200);
        }
    System.out.println("DONE.");
  }
}

堆栈跟踪:

Exception in thread "B" Exception in thread "A"  
java.util.NoSuchElementException    
    at java.util.LinkedList.removeFirst(Unknown Source)    
    at com.qed.Store.remove(Store.java:46)    
    at com.qed.Consumer.run(Consumer.java:20)    
    at java.lang.Thread.run(Unknown Source)    
java.util.NoSuchElementException    
    at java.util.LinkedList.removeFirst(Unknown Source)    
    at com.qed.Store.remove(Store.java:46)    
    at com.qed.Consumer.run(Consumer.java:20)    
    at java.lang.Thread.run(Unknown Source)    

3 个答案:

答案 0 :(得分:1)

您还必须锁定添加。您的代码允许生产者更新队列,而消费者可能想要删除条目!

当两个线程并行修改同一队列时,所有投注都会关闭!

单一锁定使用只会阻止多个消费者相互踩踏!

因此:为添加值的部分添加相同类型的锁定。

除此之外,EJP是正确的 - 一个真正的解决方案将使用低级信令方法,如wait()和notify()。但是,当然,使用这些会导致一种非常不同的行为。

并给出你的评论:请记住,这些是两个不同的东西:A)消费者/生产者彼此发送信号B)消费者/生产同步的同步。

我知道你不想要&#34; A)&#34; - 但你需要&#34; B)&#34 ;;否则你的队列会被破坏,并会出现意外。

答案 1 :(得分:0)

您应该在此处调用 wait()方法 wait()让你的线程等到其他一些线程调用通知唤醒他 sleep()根本不执行指定时间段的下一个语句 如果您看到程序片段,则使用同步块,该块使用对象检查监视器的可用性。但是您没有使用任何对象监视器方法 wait / notify / notifyAll ,并且您尝试获取并释放锁定而不调用这些方法。由于,消费者和生产者使用的列表对象,您应该使用此列表对象监视器来同步所有线程。如果一个线程获得了它的监视器,那么其他线程将无法访问它。因为每个对象只有一个监视器。这种方法将解决所有工作线程之间的同步问题。

答案 2 :(得分:0)

问题是你的Store类实现。在添加和删除元素时,您需要在那里实现wait()notify机制,而不是休眠。

您在所有消费者和制作人之间共享单个Store实例是正确的,但您的商店需要表现得像BlockingQueue

因此,要么使用JDK中的BlockingQueue的现有实现,要么修改Store类以实现类似的机制。

implement-your-own blocking queue in java

希望它有所帮助!!