java基礎:多線程之生產者消費者問題

package com.lesson3;

public class Product {
    private String name;
    private int count=1;
    private boolean flag=false;

    //使用while替代if,使用notify替代notifyAll
    public synchronized void produce(String name){
        //1.使用if會產生的問題:會產生有的產品沒有被消費,有的產品被消費多次
        //  出錯原因:此處等待的線程被喚醒後,不再判斷flag,直接往下執行   
        //  解決方法:爲使用while循環判斷flag,使得每次醒了的線程都繼續判斷flag
        /*  拋棄:if(flag)
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/

        while(flag)
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        this.name=name+count++;
        System.out.println(Thread.currentThread().getName()+"........Produce........"+this.name);
        flag=true;
        //2.使用notify會產生的問題:會產生死鎖  
        //  出錯原因:線程喚醒的時候會喚醒本方,而不是對方   
        //  解決方法:喚醒的時候把本方和對方一起喚醒,使用notifyAll()
        /*  拋棄:notify();*/
        notifyAll();
    }

    public synchronized void consume(){
        while(!flag)
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        System.out.println(Thread.currentThread().getName()+"..Consume.."+name);
        flag=false;
        /*拋棄:notify();*/
        notifyAll();
    }

}

總結:使用關鍵字synchronized的同步代碼塊和同步函數時,多生產多消費就使用while和notifyAll。
此方法的弊端是:把本方線程也喚醒,效率低。解決方法:使用Lock接口和Condition接口,在一個鎖上加多組監視器,具體方法如下:

package com.lesson4;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
 *  解決synchronized下使用while和notify進行多生產多消費效率低下問題。
 * 
 *  JDK1.5以後,Lock接口出現,替代了同步代碼塊或同步函數,將同步和鎖封裝成了對象,
 * 並將操作鎖的隱式方式定義到了該對象中,將隱式動作變成了顯式動作。同事更爲靈活,可以在
 * 一個鎖上加上多組監視器。
 *  
 * 格式如下:
 *    Lock l = ...; 
 *    l.lock();
 *    try {
 *        // access the resource protected by this lock
 *    } finally {
 *        l.unlock();
 *    }
 *    
 *  Condition接口出現替代了Object中的wait、notify和notifyAll方法,
 * 將這些監視器方法單獨進行了封裝,變成Condition監視器對象,可以和任意鎖進行組合。
 *    
 *  所有線程使用同一個鎖,但是使用不同的監視器。通過已有的鎖獲取兩組監視器,一組監視生產者,一組監視消費者。
 */
public class Product {
    private String name;
    private int count=1;
    private boolean flag=false;
    Lock lock=new ReentrantLock();

    Condition producer_con=lock.newCondition();//生產者監視器
    Condition consumer_con=lock.newCondition();//消費者監視器

    public void produce(String name){
        lock.lock();
        try{
            while(flag)
                    try {
                        producer_con.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
            this.name=name+count++;
    System.out.println(Thread.currentThread().getName()+"........Produce........"+this.name);
            flag=true;
            consumer_con.signal();//喚醒對方,且只喚醒對方一個線程
        }finally{
            lock.unlock();
        }
    }

    public void consume(){
        lock.lock();
        try{
            while(!flag)
                try {
                    consumer_con.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            System.out.println(Thread.currentThread().getName()+"..Consume.."+name);
            flag=false;
            producer_con.signal();//喚醒對方,且只喚醒對方一個線程
        }finally{
            lock.unlock();
        }
    }

}
package com.lesson3;

public class Producer implements Runnable{

    private Product p;
    Producer(Product p){
        this.p=p;
    }
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            p.produce("饅頭");
        }
    }

}
package com.lesson3;

public class Consumer implements Runnable{
    private Product p;
    Consumer(Product p){
        this.p=p;
    }
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            p.consume();
        }
    }

}
package com.lesson3;

public class ProConTest {

    public static void main(String[] args){
        Product product=new Product();
        Producer producer=new Producer(product);
        Consumer consumer=new Consumer(product);

        Thread p1=new Thread(producer);
        Thread p2=new Thread(producer);
        Thread c1=new Thread(consumer);
        Thread c2=new Thread(consumer);

        p1.start();
        p2.start();
        c1.start();
        c2.start();
    }
}
發佈了40 篇原創文章 · 獲贊 7 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章