Java 线程锁和条件变量,生产者-消费者示例

Java 线程锁和条件变量,生产者-消费者示例,java,multithreading,Java,Multithreading,我必须使用多线程编写这个productconsumer应用程序。我写了下面的java代码,但是没有;I don’我想不出哪里出了问题。我还想知道我的类设计是否合适,或者我的编码风格是否合适 提前感谢 编辑 我已经修改了产品消费者代码:但它仍然有一些问题 import java.util.*; import java.lang.Thread; public class pc_example { public static void main (String [] args) { Sto

我必须使用多线程编写这个productconsumer应用程序。我写了下面的java代码,但是没有;I don’我想不出哪里出了问题。我还想知道我的类设计是否合适,或者我的编码风格是否合适

提前感谢

编辑

我已经修改了产品消费者代码:但它仍然有一些问题

import java.util.*;
import java.lang.Thread;

public class pc_example {

public static void main (String [] args) {
    Store store = new Store( 10 );
    produce p = new produce(store);
    consume c = new consume (store);
    p.start();
    c.start();
}
}

}

类消耗扩展线程{
私营机构;
私人店铺;
消费(商店){
this.store=商店;
这是0.cons=0;
}
公开作废获取(){
已同步(存储){
if(store.Q.size()==0){
试一试{
等待();
}捕获(中断异常e){}
}
否则{
int a=store.Q.remove();
System.out.println(“用户输入:+a”);
cons++;
if(store.Q.size()

}

有关完整示例,请参阅java api中的生产者-消费者示例


代码中有几个错误。首先,生产者和消费者不使用同一队列,例如,有两个队列实例。其次,
notify
wait
方法也在不同的对象上运行

让您的示例发挥作用需要几件事:

  • 只有一个队列
  • 队列的线程安全处理
  • 处理通知和等待同一对象
  • 生产者代码可以重写为:

    public void produce() {
        int i = 0;
        while (i < 100) {
            synchronized(Q) {
                if (Q.size() < max_capacity) {
                    Q.add(i);
                    System.out.println("Produced Item" + i);
                    i++;
                    Q.notify();
                } else {
                    try {
                        Q.wait();
                    } catch (InterruptedException e) {
                        System.out.println("Exception");
                    }
                }
            }
        }
    }
    
    public void product(){
    int i=0;
    而(i<100){
    同步(Q){
    if(Q.size()<最大容量){
    Q.添加(i);
    系统输出打印项次(“生产项目”+i);
    i++;
    Q.通知();
    }否则{
    试一试{
    Q.等待();
    }捕捉(中断异常e){
    System.out.println(“例外”);
    }
    }
    }
    }
    }
    
    您正在创建两个
    生产者和消费者的实例,它们都有自己的队列,因此它们之间没有共享。您不应该在类中实例化队列,而应该在外部将其作为构造函数参数提供

    class Producer_Consumer extends Thread {
    
     private final Queue<Integer> queue;
    
     Producer_Consumer(int mode, Queue<Integer> queue)
     {
        this.queue = queue;
     }
    
     public static void main(String[] args)
     {
       Queue<Integer> queue = new LinkedQueue<Integer>();
       Producer_Consumer produce = new Producer_Consumer(queue, 2);
       Producer_Consumer consume = new Producer_Consumer(queue, 1);
       produce.start();
       consume.start();   
     }
    }
    
    类生产者\消费者扩展线程{
    私有最终队列;
    生产者\消费者(整数模式,队列)
    {
    this.queue=队列;
    }
    公共静态void main(字符串[]args)
    {
    Queue Queue=new LinkedQueue();
    生产者消费者产品=新生产者消费者(队列,2);
    生产者消费者消费=新生产者消费者(队列,1);
    product.start();
    consume.start();
    }
    }
    

    根据建议,可以使用
    java.util.concurrent
    包中的阻塞队列进行进一步的改进。对于此类任务,确实不需要使用对象的方法
    wait()
    notify()

    1,使用适当的类型。您的模式最好使用en枚举,而不是int。
    2、线程之间的管道Q实际上没有共享,因为它没有声明为静态。
    由于linkedlist未同步,您可能会遇到问题。

    同步
    product()
    consume()
    没有区别。

    这就是a的用途。

    您的每个对象都在一个不同的

    Queue<Integer> Q
    
    队列Q
    
    因此,制作人把东西放进一个盒子里,但消费者从来不看这个盒子——它试图从一个
    Q
    盒子里得到东西,而这个盒子里从来没有放过任何东西


    但是,一旦解决了这个问题,就需要确保以线程安全的方式处理
    队列
    对象。虽然
    product()
    consume()
    方法都是同步的,但此级别的同步不会有帮助,因为您处理的是两个不同的
    生产者和消费者
    对象。他们需要以其他方式同步对共享资源的访问。

    我建议查看java.util.concurrent(可从java 1.5获得)中的类。特别是,您可以使用BlockingQueue而不是队列

    它允许您制作:

    try {
        while(true) { queue.put(produce()); }
    } catch (InterruptedException ex) { ... handle ...}
    
    和消费:

    try {
       while(true) { consume(queue.take()); }
     } catch (InterruptedException ex) { ... handle ...}
    
    Otherwize(如果这是一个关于java同步的练习),您应该

    • 提高字段的可见性(为什么只有最大容量是专用的?)
    • 改进设计(我更喜欢为生产者和消费者创建两个单独的类)
    • 确保生产者和消费者等待并通知同一对象
    • 使生产者和消费者在同一队列上工作

      • 线程类中缺少Run方法。所以你的线程开始和结束时什么都不做。重命名要在while循环中运行和使用的put和get方法。还要注意的是,您需要调用notify并在商店(监视器)上等待


        哼,因为队列是在线程之间共享的,所以在访问之前必须锁定它@巴甫洛维奇:你能解释一下阻塞队列和java.util.concurrent包吗?@dacwe我同意,但如果你想教别人,最好一步一步地教step@AmmSokun在这里:花一些时间阅读Javadoch。一个类的两个实例试图使用共享资源执行相反的任务,这需要使用同步。我说这两个实例实际上是两个正在运行的线程,对吗。在一个实例中,product方法正在运行,而在另一个实例中,consume方法正在运行。我也同意我应该有一个全局队列。还有,如何在java中声明全局变量呢?好的,我同意存在两个不同的Q对象。但是,一旦我同步了方法本身,我就不理解同步Q对象的必要性?如果调用
        object2.notify()
        它将通知一个线程等待该对象(
        object2
        )。在您(当前)的示例中
        Queue<Integer> Q
        
        try {
            while(true) { queue.put(produce()); }
        } catch (InterruptedException ex) { ... handle ...}
        
        try {
           while(true) { consume(queue.take()); }
         } catch (InterruptedException ex) { ... handle ...}
        
        public void run() {
        
                while(true){
                synchronized (store) {
                    if (store.Q.size() > store.max_capacity) {
                        try {
                            store.wait();
                        } catch (InterruptedException e) {}
                    }
                    else {
                        element ++;
                        System.out.println( "Producer put: " + element );
                        store.Q.add(element);
                        store.notify();
                    }               
                }
            }
            }