Java Producer Consumer模型总是死锁

时间:2015-03-01 15:41:08

标签: java multithreading deadlock

所以我有这样的帮助来制作制作人消费者模型作业,我完成了一个非常粗糙的版本(但我能用现有的Java技能做的最好)。

它似乎有效,但遇到了死锁问题http://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem Wiki链接描述的内容,基本上由于某种原因,最终所有线程都入睡并且无法唤醒彼此进入永恒的睡眠周期。

我不确定我的代码究竟是什么造成了这种情况,因为我会想到我写这篇文章的方式不会发生这种情况,但是我仍然不会这样做100%了解Threads的工作原理。

这是我的代码:

package boundedbuffer;

import java.util.LinkedList;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Queue;


public class BoundedBuffer {


    public static int CapacityCheck = 0;


    public static void main(String[] args){


        MessageQueue queue = new MessageQueue(3); // <-- max capacity of queue is given here as 3

        Thread t1 = new Thread(new Producer(queue));
        Thread t2 = new Thread(new Producer(queue));
        Thread t3 = new Thread(new Producer(queue));  
        Thread t4 = new Thread(new Consumer(queue));
        Thread t5 = new Thread(new Consumer(queue));
        Thread t6 = new Thread(new Consumer(queue));
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();

    }
}

public class Producer implements Runnable{

    private MessageQueue queue;

    private static String msgs[] = {
            "some test message",
            "long message",
            "short message",
            "yet another message"
        };

    public Producer(MessageQueue queue){
        this.queue = queue;
    }

    @Override
    public synchronized void run() {
        while(true){
            Random rand = new Random();
            int wait = rand.nextInt(3000);
            int index = rand.nextInt(4);
            try {
                Thread.sleep(wait);
            } catch (InterruptedException ex) {
                Logger.getLogger(Producer.class.getName()).log(Level.SEVERE,
                null, ex);
            }         
            if(BoundedBuffer.CapacityCheck < queue.capacity){ 
                System.out.println("Puts into buffer: " + msgs[index]);
                queue.put(msgs[index]);
                BoundedBuffer.CapacityCheck++;
                notifyAll();
            }else{
                try {
                    wait();
                } catch (InterruptedException ex) {
                    Logger.getLogger(Producer.class.getName()).log(Level.SEVERE,                        null, ex);            
                }
            }
        }

    }

}

public class Consumer implements Runnable{

    private MessageQueue queue;

    public Consumer(MessageQueue queue){
        this.queue = queue;
    }

    @Override
    public synchronized void run() {
        while(true){
            Random rand = new Random();
            int wait = rand.nextInt(3000);
            try {
                Thread.sleep(wait); 
            } catch (InterruptedException ex) {
                Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex);
            }
            String msg = queue.get();
            if(msg == null){
                try {
                    wait();
                } catch (InterruptedException ex) {
                    Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            queue.get();
            BoundedBuffer.CapacityCheck--;
            System.out.println("Takes out of buffer: " + msg);
            notifyAll();        
        }
    }


}


public class MessageQueue  {

    public final int capacity;
    private final Queue<String> messages = new LinkedList<>();


    public MessageQueue(int capacity) {
        this.capacity = capacity;
    }

    public void put(String msg){
        this.messages.add(msg);
    }

    public String get(){ 
        if(messages.isEmpty()){ 
            return null;
        }else{
            String msg = messages.element();
            messages.remove();
            return msg;
        }
    }
}

另一个小但有趣的问题是,我要么或者只是曾经只看过一次&#34;带出一件物品&#34;彼此发生了不止一次。放入物品总是发生一次,两次或最多三次(我为此示例制作了缓冲区大小3,因此它不会发生4次)但是取出一个项目可能只发生一次,然后之后它总是把一个放回去,取出一个,放回一个。我投入3件物品之后就没见过:拿出一件,再拿一件,例如。

这可能是一个问题或错误。 IDK。

我还认为在run方法上使用Synchronized感觉有点不对,但如果我把它拿出来,那么我会得到一个IllegalMonitorState异常。

我使用多个制作人和多个消费者,因为我的老师要求我们这样做。

1 个答案:

答案 0 :(得分:2)

你所有的线程停滞都是因为你正在为传递给线程的不同生产者和消费者获得互斥。

您在运行方法上同步意味着在调用wait方法时获取不同对象上的互斥锁并进入阻塞状态,假设有人会通知线程返回。即使其他线程通知他们通知这个(个体生产者或消费者)实例而不是生产者和消费者之间的共享实例。

像您一样MessageQueue共享公共实例并在队列上而不是在run方法上进行同步。