刚学java的多线程写了一个生产者消费者的小例子,但是不太确定有没有问题,哪位高手看看有问题或可以改进的地方请指出来,不胜感激!!!! 
package thread;import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;public class CustomerProducterThread {    public static void main(String[] args) {
        Storage storage = new Storage();
        Customer c1 = new Customer(storage);
        c1.setName("c1");
        Customer c2 = new Customer(storage);
        c2.setName("c2");
        Customer c3 = new Customer(storage);
        c3.setName("c3");
        Customer c4 = new Customer(storage);
        c4.setName("c4");
        Producter p1 = new Producter(storage);
        p1.setName("p1");
        Producter p2 = new Producter(storage);
        p2.setName("p2");
        Producter p3 = new Producter(storage);
        p3.setName("p3");
        Producter p4 = new Producter(storage);
        p4.setName("p4");
        c1.start();
        c2.start();
        p1.start();
        p2.start();
        p3.start();
        p4.start();
        c3.start();
        c4.start();    }
}class Storage {//仓库    public static final int[] lock = new int[0];
    public static final Integer max = 100;
    private Integer sto = 0;    public void put(int i, String name) {//生产者放
        synchronized (lock) {
            if ((this.sto + i) > max) {
                System.out.println(name + "仓库已满............" + i);
                try {
                    lock.wait();
                    put(i,name);//等wait结束后继续调用put方法来放置刚才生产的i个产品
                } catch (InterruptedException ex) {
                    Logger.getLogger(Storage.class.getName()).log(Level.SEVERE, null, ex);
                }
            } else {
                this.sto = this.sto + i;
                System.out.println(name + "生产了" + i + "个产品,现在库存量" + this.sto);
                lock.notifyAll();
            }
        }
    }    public void get(int i, String name) {//消费者取
        synchronized (lock) {
            if (this.sto - i < 0) {
                System.out.println(name + "仓库余额不足............" + i);
                try {
                    lock.wait();
                    get(i,name);
                } catch (InterruptedException ex) {
                    Logger.getLogger(Storage.class.getName()).log(Level.SEVERE, null, ex);
                }
            } else {
                this.sto = this.sto - i;
                System.out.println(name + "消费了" + i + "个产品,现在库存量" + this.sto);
                lock.notifyAll();
            }
        }
    }
}class Customer extends Thread {//消费者    public Storage sto;    public Customer(Storage sto) {
        this.sto = sto;
    }    @Override
    public void run() {
        while (true) {
            sto.get(new Random().nextInt(10) + 1, Thread.currentThread().getName());
        }    }
}class Producter extends Thread {//生产者    public Storage sto;    public Producter(Storage sto) {
        this.sto = sto;
    }    @Override
    public void run() {
        while (true) {
            sto.put(new Random().nextInt(10) + 1, Thread.currentThread().getName());
        }    }
}

解决方案 »

  1.   

    synchronized (lock) { //用synchronized(this)就可以了,因为你的线程使用的都是同一个Store对象
    put(i,name);//递归调用,有什么特别意义吗?多余的处理,把else去掉就可以了,wait结束后,直接执行else部分的代码,即
            synchronized (lock) {
                if ((this.sto + i) > max) {
                    System.out.println(name + "仓库已满............" + i);
                    try {
                        lock.wait();
                        //put(i,name);//等wait结束后继续调用put方法来放置刚才生产的i个产品
                    } catch (InterruptedException ex) {
                        Logger.getLogger(Storage.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } //else {
                    this.sto = this.sto + i;
                    System.out.println(name + "生产了" + i + "个产品,现在库存量" + this.sto);
                    lock.notifyAll();
                //}
            }象生产者消费者对象,可以使用数组,没必要一个一个处理,多麻烦,用数组管理,循环处理方便一些另外,因为你是调用random来表示产品编号的,这样你的生产者和消费者可能产生的编号不一致,你这个random有什么特别的意义吗?
      

  2.   

    谢谢各位耐心看完代码。回答一下qybao的几个疑问
    1.用synchronized (lock)是因为我觉的我用的c1 c2 p1 p2等都是 不同的实例如果用synchronized (this)的话同步会不会不起作用呢,这个我不是很清楚。
    2.递归调用我绝的还是有必要的,因为如果现存量为98这时生产者又生产了4个那么就会使用wait()阻塞了,但是当别的消费者消费了1个后就会notifyAll这时那个原来没执行完的方法又从wait()往下执行,会使仓库的量变为101这样就错了。
    3.random没有什么实际意义只是用来使消费者随便取,生产者随便放用的,好测试。
      

  3.   

    我感觉是有问题的。   public void put(int i, String name) {//生产者放
            synchronized (lock) {
                if ((this.sto + i) > max) {
                    System.out.println(name + "仓库已满............" + i);
                    try {
                        lock.wait();
                        put(i,name);//等wait结束后继续调用put方法来放置刚才生产的i个产品
                    } catch (InterruptedException ex) {
                        Logger.getLogger(Storage.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } else {
                    this.sto = this.sto + i;
                    System.out.println(name + "生产了" + i + "个产品,现在库存量" + this.sto);
                    lock.notifyAll();
                }
            }
        }    public void get(int i, String name) {//消费者取
            synchronized (lock) {
                if (this.sto - i < 0) {
                    System.out.println(name + "仓库余额不足............" + i);
                    try {
                        lock.wait();
                        get(i,name);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(Storage.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } else {
                    this.sto = this.sto - i;
                    System.out.println(name + "消费了" + i + "个产品,现在库存量" + this.sto);
                    lock.notifyAll();
                }
            }
        }
    }你把同步都做在了 lock 上那你想想下面的情况,当容量到达100的时候 比如正有 5个写线程在 lock 上 wait  这时候, 消费者执行 ,并 notifyAll 系统选择了一个生产者来执行, 此时生产者生产了一个产品,但是此时它 notifyAll 就通知了所有在 lock 上等待的对象, 包括等待的写着线程。 写着线程可以唤醒写着线程这就是问题的所在。
    其实这是个既有同步又有互斥的例子,但是在使用 synchronized 实现的时候我们可以把他当成一个同步的例子。你只需要把你的同步对象改了就行了。  比如
      synchronized (lock) {
                if ((this.sto + i) > max) {
                    System.out.println(name + "仓库已满............" + i);
                    try {
                        lock.wait();
                        put(i,name);//等wait结束后继续调用put方法来放置刚才生产的i个产品
                    } catch (InterruptedException ex) {
                        Logger.getLogger(Storage.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } else {
                    this.sto = this.sto + i;
                    System.out.println(name + "生产了" + i + "个产品,现在库存量" + this.sto);
                    lock1.notifyAll();
                }
            }
      if (this.sto - i < 0) {
                    System.out.println(name + "仓库余额不足............" + i);
                    try {
                        lock1.wait();
                        get(i,name);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(Storage.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } else {
                    this.sto = this.sto - i;
                    System.out.println(name + "消费了" + i + "个产品,现在库存量" + this.sto);
                    lock.notifyAll();
                }
      

  4.   


    我并没有自习看你的递归调用, 但是 通过  在不同对象上同步可以解决 生产者唤醒生产者的问题。synchronized(this)不行是因为  this 表示本对象的引用,每个线程都在本对象上同步 等于没做同步。你共享的是  sto对象  我上面的代码有问题 ,  忘记改 同步对象了 一会给你贴正确的。  同步要做在 sto 上 。这里需要两个同步语句。
      

  5.   

    哈哈 上面的代码有问题我刚写的这个
    package thread;import java.util.Random;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    import java.util.logging.Level;
    import java.util.logging.Logger;public class CustomerProducterThread {    public static void main(String[] args) throws InterruptedException {
            Storage storage = new Storage();
     
            ExecutorService exec=Executors.newCachedThreadPool();
            for(int i=0;i<4;i++){
             exec.execute(new Producter(storage));
             exec.execute(new Customer(storage));
            }
            
            TimeUnit.SECONDS.sleep(2);
            exec.shutdownNow();
                }
    }class Storage {//仓库    public static final int[] lock = new int[0];
        public static final int[] lock1= new int[0]; 
        public static final Integer max = 100;
        
        private Integer sto = 0;    public void put(int i, String name) {//生产者放
            synchronized (lock) {
                if ((this.sto + i) > max) {
                    System.out.println(name + "仓库已满............" + i);
                    try {
                        lock.wait();
                      //  put(i,name);//等wait结束后继续调用put方法来放置刚才生产的i个产品
                    } catch (InterruptedException ex) {
                        Logger.getLogger(Storage.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } else {
                    this.sto = this.sto + i;
                    System.out.println(name + "生产了" + i + "个产品,现在库存量" + this.sto);
                 
                }
            }
            synchronized(lock1){  
                 
                lock1.notifyAll();  
            }      }    public void get(int i, String name) {//消费者取
            synchronized (lock1) {
                if (this.sto - i < 0) {
                    System.out.println(name + "仓库余额不足............" + i);
                    try {
                        lock1.wait();
                       // get(i,name);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(Storage.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } else {
                    this.sto = this.sto - i;
                    System.out.println(name + "消费了" + i + "个产品,现在库存量" + this.sto);
                   
                }
            }
            synchronized(lock){  
                
                lock.notifyAll();  
            }  
        }
    }class Customer extends Thread {//消费者    public Storage sto;    public Customer(Storage sto) {
            this.sto = sto;
        }    @Override
        public void run() {
            while (!Thread.interrupted()) {
                sto.get(new Random().nextInt(10) + 1, Thread.currentThread().getName());
            }    }
    }class Producter extends Thread {//生产者    public Storage sto;    public Producter(Storage sto) {
            this.sto = sto;
        }    @Override
        public void run() {
            while (!Thread.interrupted()) {
                sto.put(new Random().nextInt(10) + 1, Thread.currentThread().getName());
            }    }
    }
      

  6.   

    1
    你要知道synchronized的意义,就是对一个对象加锁,你的生产者和消费者对象都使用同一个Storage对象storage,所以对这个对象同步就可以了,而你的线程刚好就是调用这个对象的方法,所以synchronized(this)就可以了,这里的this就是storage对象2
    我上面理解错你的意思了,是为了让总数不超过一定的数量,所以还是需要else,不过递归没必要,因为你的线程是循环执行的,所以下一次还会进入方法,只不过这时的i可能改变了,除非你希望原来的i不改变。3
    random的意义我原来看错了,不是生产消费的编号,而是随机生产和消费的数量。
      

  7.   


    如果只用一个锁 ,会出现 生产者 通知生产者的情况   所以肯定不能只在 sto 上面加锁。多人多缓生产者消费者 问题 。至少需要用两个锁来实现
      

  8.   


    生产者通知生产者有问题吗?被通知的生产者,不满足条件同样进入wait让出CPU,所以没有特殊必要,不需要采用多个锁,在notifyAll的时候,同样都存在生产者或消费者。一般的多线程,多采用队列来处理,包括线程池也有自己的队列,按你这么说,也要采用多个队列吗?具体问题具体分析,不是抱着理论人云亦云。像你的代码,采用2把锁,能体现出什么?如果生产者和消费者两个线程分别进入else代码,你还能保证同步吗?因为生产者和消费者线程锁的对象不一样,所以很有可能生产者线程和消费者线程同时进入else,因为你的代码,只是生产者排斥生产者线程(即某个生产者线程执行,另一个生产者线程没有锁权而等待),消费者排斥消费者线程,但是生产者和消费者线程并不排斥(即生产者线程执行,消费者线程也执行,这样你的库存量的控制可以说是随机的,并不是可控的),你再好好理解一下吧。
      

  9.   


    什么叫抱着理论人云亦云? 操作系统人家总结了这么多年还没你说的对?针对你的问题 我承认我代码有问题  ,看新代码?
    package thread;import java.util.Random;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    import java.util.logging.Level;
    import java.util.logging.Logger;public class CustomerProducterThread {    public static void main(String[] args) throws InterruptedException {
            Storage storage = new Storage();
     
            ExecutorService exec=Executors.newCachedThreadPool();
            for(int i=0;i<4;i++){
             exec.execute(new Producter(storage));
             exec.execute(new Customer(storage));
            }
            
            TimeUnit.SECONDS.sleep(2);
            exec.shutdownNow();
                }
    }class Storage {//仓库    public static final int[] lock = new int[0];
        public static final int[] lock1= new int[0]; 
        public static final Integer max = 100;
        
        private Integer sto = 0;    public void put(int i, String name) {//生产者放
            synchronized (lock) {
                if ((this.sto + i) > max) {
                    System.out.println(name + "仓库已满............" + i);
                    try {
                        lock.wait();
                      //  put(i,name);//等wait结束后继续调用put方法来放置刚才生产的i个产品
                    } catch (InterruptedException ex) {
                        Logger.getLogger(Storage.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } else {
                 synchronized(this){
                    this.sto = this.sto + i;
                    System.out.println(name + "生产了" + i + "个产品,现在库存量" + this.sto);
                 }
                }
            }
            synchronized(lock1){  
                 
                lock1.notifyAll();  
            }      }    public void get(int i, String name) {//消费者取
            synchronized (lock1) {
                if (this.sto - i < 0) {
                    System.out.println(name + "仓库余额不足............" + i);
                    try {
                        lock1.wait();
                       // get(i,name);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(Storage.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } else {
                 synchronized(this){
                    this.sto = this.sto - i;
                    System.out.println(name + "消费了" + i + "个产品,现在库存量" + this.sto);
                 }
                   
                }
            }
            synchronized(lock){  
                
                lock.notifyAll();  
            }  
        }
    }class Customer extends Thread {//消费者    public Storage sto;    public Customer(Storage sto) {
            this.sto = sto;
        }    @Override
        public void run() {
            while (!Thread.interrupted()) {
                sto.get(new Random().nextInt(10) + 1, Thread.currentThread().getName());
            }    }
    }class Producter extends Thread {//生产者    public Storage sto;    public Producter(Storage sto) {
            this.sto = sto;
        }    @Override
        public void run() {
            while (!Thread.interrupted()) {
                sto.put(new Random().nextInt(10) + 1, Thread.currentThread().getName());
            }    }
    }
      

  10.   

    这个问题在操作系统上都学过, 我刚开始想的有点简单,对于 storage 加锁 我并没有意识到 。
      

  11.   


    我的回复是针对你的回复
    如果只用一个锁 ,会出现 生产者 通知生产者的情况 所以肯定不能只在 sto 上面加锁。
    叫你不要死抱着理论而不会变通,要具体问题具体分析,在这个问题上,你的多把锁体现不出任何优势。因为上面也说了,生产者和消费者可以同时进入方法,可以分别执行notifyAll,这样同样存在着生产者和消费者线程去竞争CPU。
      

  12.   


        private final ReentrantLock lock;
        /** Condition for waiting takes */
        private final Condition notEmpty;
        /** Condition for waiting puts */
        private final Condition notFull;[
    这是生产者消费者队列的属性 , 至少它也用来 两个 Condition  ,如果按你的意思它是不是只用一个 condition就行了?   明显的同步问题你为什么 会让生产者去唤醒生产者?
      

  13.   


    至少 生产者 不会去通知生产者吧?  你看看我的例子并行度是要比你说的高吧? 你把整个缓冲区都加锁了 ,至少生产者和消费者可以 在都满足条件的时候同时执行, 你那个把锁 直接加到 storage 上了,整个程序都被线性话了
      

  14.   


    我到觉得你说的不成立,  
    因为上面也说了,生产者和消费者可以同时进入方法,可以分别执行notifyAll,这样同样存在着生产者和消费者线程去竞争CPU
    这中情况下的竞争是不可避免的
      

  15.   

    啊!!!!!!!第一次结贴,给错分了!!!qybao见谅啊!!!
      

  16.   

    在调用notifyAll的时候也要加锁吗?