多線程實踐-生產者消費者

        當多個線程操作同一個資源,但是操作的動作不同時,就會需要線程間進行通信。很著名的是生產者消費者的例子。

        有一個盤子,只能放一片面包,生產者生產麪包放入盤子,消費者從盤子中取走麪包吃掉。

        由簡單開始,i+1。先看一個生產者、一個消費者。

代碼如下:

public class ProducerConsumerDemo {
    public static void main(String[] args){
        Resource r = new Resource();

        new Thread(new Producer(r)).start();
        new Thread(new Consumer(r)).start();
    }
}

class Resource{  //公共資源
    private String name;
    private int count =1;
    private boolean flag = false;

    public synchronized void set(String name){
        if(flag)
            try{this.wait();}catch(Exception e){}
        this.name = name + "--" + count++;
        System.out.println(Thread.currentThread().getName() + "...生產者:生產了"+this.name);
        flag = true;
        notify();
    }
    public synchronized void out(){
        if(!flag)
            try{this.wait();}catch(Exception e){}
        System.out.println(Thread.currentThread().getName() + "...消費者:消費了"+this.name);
        flag = false;
        notify();
    }
}
class Producer implements Runnable{
    private Resource res;
    Producer(Resource res){
        this.res = res;
    }
    public void run(){
        while(true){
            res.set("麪包");
        }
    }
}
class Consumer implements Runnable{
    private Resource res;
    Consumer(Resource res){
        this.res = res;
    }
    public void run(){
        while(true){
            res.out();
        }
    }
}

        運行結果如圖:


        由運行結果可以看到。Thread-0和Tread-1兩個線程是交替進行,生產者生產商品i,消費者就把商品i消費掉。然後生產者生產商品i+1,消費者再消費商品i+1。

        本來我自己想的實現過程是這樣的:對於盤子來說,它有兩種狀態,可以往裏放麪包和不可以放麪包。生產者來了,如果可放,就生產一個麪包,並把盤子置爲不可放麪包的狀態,如果不可放,就什麼都不操作。消費者來了,如果可放(就代表不能取),就什麼都不操作,如果不可放(代表能取),就取走一個麪包,並把盤子置爲可放麪包狀態。代碼如下:

class Resource{  //公共資源
    private String name;
    private int count =1;
    private boolean flag = false;

    public synchronized void set(String name){
        if(flag)
        {
        }
          //try{this.wait();}catch(Exception e){}
        else{
            this.name = name + "--" + count++;
            System.out.println(Thread.currentThread().getName() + "...生產者:生產了"+this.name);
            flag = true;
        }
        //notify();
    }
    public synchronized void out(){
        if(!flag){
        }
        //try{this.wait();}catch(Exception e){}
        else{
            System.out.println(Thread.currentThread().getName() + "...消費者:消費了"+this.name);
            flag = false;
        }

        //notify();
    }
}

        與上面的示例中代碼的區別是沒有使用wait()和notify()方法。一樣能實現效果。看圖:



        不用使用wait()和notify()與使用有什麼區別呢?既然它存在,肯定是有它的道理的。猜測它的優點是效率更高。用什麼方法可以驗證一下?嘗試着加了運行時間和打印輸出。如圖:

        (1)不用wait()/notify()


        (2)用wait()/notify()


        count爲10000時,不使用wait()和notify()生產9999個麪包需要1330ms,而使用wait()和notify()生產9999個麪包只需要406ms。多次執行,每次的結果相差不大。

增加一下數量級,再比比,也很明顯:6704msVS 3208ms。

         

(1)不用wait()/notify()(2)用wait()/notify()                   



        計時代碼增加到了main方法所在類和公共資源類下,代碼如下:

public class ProducerConsumerDemo {
    public static long strateTime;
    public static void main(String[] args){
        Resource r = new Resource();
        strateTime = System.currentTimeMillis();
        new Thread(new Producer(r)).start();
        new Thread(new Consumer(r)).start();
    }
}

class Resource{  //公共資源
    public static long endTime;
    private String name;
    private int count =1;
    private boolean flag = false;

    public synchronized void set(String name){
        if(flag)
//        {
//           System.out.println("無效執行");
//        }
            try{this.wait();}catch(Exception e){}
        else{
            this.name = name + "--" + count++;
            if(count==100000){
                endTime= System.currentTimeMillis();
                System.out.println("程序運行時間:" + ( endTime - ProducerConsumerDemo.strateTime )+"ms");
            }
            System.out.println(Thread.currentThread().getName() + "...生產者:生產了"+this.name);
            flag = true;
        }
        notify();
    }
    public synchronized void out(){
        if(!flag)
//        {
//            System.out.println("無效執行");
//        }
            try{this.wait();}catch(Exception e){}
        else{
            System.out.println(Thread.currentThread().getName() + "...消費者:消費了"+this.name);
            flag = false;
        }

        notify();
    }
}

        對比發現,差不多兩倍的差距,效率是不一樣的。是因爲啥呢?

        //TODO:

       

多線程方法彙總:

        等待喚醒:wait()、notify()、notifyAll()。

        waite()是把線程由運行狀態置爲等待狀態,等待線程都存在線程池中。

        notify()方法是把等待狀態的線程從線程池中喚醒。通常喚醒線程池中的第一個等待的線程。

        notifyAll()是把線程池中的所有等待線程都喚醒。






發佈了161 篇原創文章 · 獲贊 116 · 訪問量 41萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章