为什么Producer / Consumer线程演示挂在错误的地方?

时间:2017-03-25 01:58:34

标签: java multithreading producer-consumer

我尝试编写一个Producer / Consumer线程演示,但结果证明是错误的。

Consumer.class

public void consume(){
    if(TaskQueue.getInstance().isEmpty()){
        System.out.println("B1  "+Thread.currentThread().getName());
        synchronized (pruductMonitor) {
            System.out.println("B2  "+Thread.currentThread().getName());
            if(TaskQueue.getInstance().isEmpty()){
                //如果任务空了
                //唤醒生产者
                System.out.println("notify A    "+Thread.currentThread().getName());
                pruductMonitor.notify();

            }
            System.out.println("B3  "+Thread.currentThread().getName());
            synchronized (customerMonitor) {
                System.out.println("B4  "+Thread.currentThread().getName());
                if(TaskQueue.getInstance().isEmpty()){
                    //挂起消费者
                    try {
                        System.out.println("B wait  "+Thread.currentThread().getName());
                        customerMonitor.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
    }else{
        System.out.println("B running   "+Thread.currentThread().getName());
        TaskQueue.getInstance().getTask();
    }
}

Producer.class

private void produce() {
    Bean b = new Bean();
    if (TaskQueue.getInstance().isFull()) {
        System.out.println("A1  "+Thread.currentThread().getName());
        synchronized (customerMonitor) {
            System.out.println("A2  "+Thread.currentThread().getName());
            if (TaskQueue.getInstance().isFull()) {
                // 如果队列满了,唤醒消费者
                System.out.println("notify B... "+Thread.currentThread().getName());
                customerMonitor.notify();

            }
            System.out.println("A3  "+Thread.currentThread().getName());
            synchronized (pruductMonitor) {
                System.out.println("A4  "+Thread.currentThread().getName());
                if (TaskQueue.getInstance().isFull()) {
                    try {
                        System.out.println("A wait  "+Thread.currentThread().getName());
                        pruductMonitor.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }

        }
    } else {
        System.out.println("A running   "+Thread.currentThread().getName());
        TaskQueue.getInstance().addTask(b);
    }
}

Main.class

public class Main {

/**
 * @param args
 */
public static void main(String[] args) {
    // TODO Auto-generated method stub
    Object pruductMonitor = new Object();
    Object customerMonitor = new Object();

    TaskQueue.getInstance().setLimit(10);

      //生产者开动  
    new Thread(new Pruduct(pruductMonitor,customerMonitor)).start();  
    new Thread(new Pruduct(pruductMonitor,customerMonitor)).start();  
    new Thread(new Pruduct(pruductMonitor,customerMonitor)).start();  
    new Thread(new Pruduct(pruductMonitor,customerMonitor)).start();  
    //消费者开动  
    new Thread(new Customer(pruductMonitor,customerMonitor)).start();  
    new Thread(new Customer(pruductMonitor,customerMonitor)).start();  

    new Thread(new Runnable() {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(true){
                System.out.println("==============="+TaskQueue.getInstance().getNum()+"===============");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }).start();
}  

}

结果是

=============== 9 ===============

正在运行的Thread-2

正在运行的Thread-0

正在运行的Thread-3

A1 Thread-1

A2 Thread-1

通知B ... Thread-1

A3 Thread-1

A4 Thread-1

B运行Thread-4

等待Thread-1

正在运行的Thread-2

B运行Thread-4

=============== 9 ===============

B运行Thread-5

正在运行的Thread-0

正在运行的Thread-3

B运行Thread-5

B运行Thread-4

A1 Thread-3

A1 Thread-2

A1 Thread-0

=============== 10 ===============

B运行Thread-5

=============== 8 ===============

B运行Thread-4

=============== 6 ===============

B运行Thread-5

B运行Thread-4

=============== 4 ===============

B运行Thread-4

B运行Thread-5

=============== 2 ===============

B运行Thread-4

B运行Thread-5

0 =============== ===============

B1 Thread-5

B1 Thread-4

B2 Thread-5

通知线程-5

B3 Thread-5

0 =============== ===============

0 =============== ===============

0 =============== ===============

0 =============== ===============

0 =============== ===============

为什么在Producer.class中运行pruductMonitor.wait()之后,所有生产者线程都挂在synchronized(customerMonitor)上,是否应该挂起synchronized(pruductMonitor)?

1 个答案:

答案 0 :(得分:1)

您有几个可能的问题。

总是在循环中等待,如:

while (!somecondition) 
    somelock.wait();

等待线程释放锁定;一旦线程唤醒并重新获取锁定,它需要再次检查条件。线程获得通知和唤醒的时间与该线程可以获得锁定的时间之间可能发生很多事情,导致线程被通知的状态可能会在该时间间隔内再次发生变化。

其次,在一个地方以一个顺序获取多个锁,在另一个地方以相反的顺序获取多个锁是导致死锁的好方法。如果你想要的是确保通知总是影响相关的线程(消费者不会得到生产者相关条件的通知,反之亦然),最好使用具有两个独立条件的ReentrantLock而不是像这样处理两个内部锁。请参阅Condition类的API。

这太复杂了,很大程度上是因为锁定与被访问的数据结构无关。锁定的目的是保护数据结构免受线程的不安全操纵。将锁定放在任务队列中,而不是放在生产者和消费者中,这将变得更易于管理。