1

我尝试了一个生产者消费者程序..有时输出是正确的,有时虽然程序运行完成,但输出中间会出现异常..

我明白IndexOutOfBoundsException了,我相信原因如下:-当 Q 为空时,所有 3 个消费者线程都进入等待状态;当生产者添加一个项目并通知所有等待线程时,在消费者线程删除该项目后,唤醒的另一个消费者线程将尝试删除(当 Q 现在为空时)导致此问题.. 我知道这是一场比赛条件,但无法弄清楚如何避免它......欢迎任何想法/建议。

另一个查询 - 我无法找到优雅终止该程序的方法。截至目前,我已经使用System.exit(0)了最后一个项目的生产时间。欢迎任何其他更好的想法。

附言

我不想使用任何 java 的 API 同步类,我想尝试使用wait()/notify()机制..

class Producer implements Runnable
{
    private Queue q;
    Producer(Queue q)
    {
        this.q = q;
    }
    @Override
    public void run() { 
        for(int i =0;i<50;i++)
            try {
                q.add(new Integer(i));
            } catch (InterruptedException e) {  
                e.printStackTrace();
            }   
    }
}
class Consumer extends Thread
{
    private Queue q;
    Consumer(Queue q)
    {
        this.q = q;
    }   
    public void run()
    {       
        try {
            while(true)
            {
            System.out.println(Thread.currentThread().getName()+"-"+ q.get());
            }
        } catch (InterruptedException e) {      
            e.printStackTrace();
        }
    }
}
public class Main 
{
    public static void main(String args[])
    {
    Queue q = new Queue();
    Producer runObj = new Producer(q);
    Thread producerObj = new Thread(runObj,"Producer");
    producerObj.start();    
    Consumer c1 = new Consumer(q);
    Consumer c2 = new Consumer(q);
    Consumer c3 = new Consumer(q);
    c1.setName("c1");
    c2.setName("c2");
    c3.setName("c3");
    c1.start();
    c2.start();
    c3.start(); 
    }
}

队列类:

public class Queue {

    private ArrayList<Integer> itemQ;
    int qSize = 5;

    Queue()
    {
        itemQ = new ArrayList<Integer>();
    }

    public synchronized void add(Integer item) throws InterruptedException
    {

        if(itemQ.size() == qSize)
        {
            System.out.println("Q is full");
            wait();
        }
        System.out.println(item);
        if(item.equals(new Integer(49)))
        {
            System.out.println("Out Of Stock");
            System.exit(0);

        }
        itemQ.add(item);
        notifyAll();
    }

    public synchronized Integer get() throws InterruptedException
    {
        if(itemQ.isEmpty())
        {
            wait();
        }   
        Integer item = itemQ.remove(0);     
        notify();       
        return item;
    }
}
4

1 回答 1

1

您需要更改 if 测试Queue.addQueue.get改为使用循环。例如将Queue.get方法的代码更改为

while (itemQ.isEmpty()) {
    wait();
}
Integer item = itemQ.remove(0);
notify();
return item;

当你调用 wait 时,你放弃了锁,一旦你重新获得它,你需要测试你测试的条件(在你放开锁之前)是否仍然正确。当某些东西被添加到队列中时,每个在获取队列锁时被阻止的消费者都会收到通知,并且他们中的任何一个都可以获得它。因此,在线程从等待中唤醒到它重新获取锁的时间之间的时间间隔内,另一个消费者可以潜入并从队列中删除某些内容(使您的队列不能为空的假设无效)。

还有一些虚假的唤醒,即使没有应用程序事件导致线程也可以获得通知。这是在醒来时检查状态的另一个原因。

于 2013-09-20T18:14:11.490 回答