<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    生產者與消費者(多線程經典案例)

    注:此示例來自MLDN講師李興華JAVA SE基礎教學部分

    生產者和消費者是多線程中一個經典的操作案例下面一起看下代碼:

    示例一:

    package org.lx.multithreading;

    /**
     * 定義一個信息類
     * 
    @author Solitary
     
    */
    class Info {    
        
    private String name = "羅星" ;            //定義name屬性        
        private String content ="JAVA初學者";    //定義content屬性
        
        
    //getter     setter
        public String getName() {
            
    return name;
        }
        
    public void setName(String name) {
            
    this.name = name;
        }
        
    public String getContent() {
            
    return content;
        }
        
    public void setContent(String content) {
            
    this.content = content;
        }
    } ;

    /**
     * 定義生產者
     * 
    @author Solitary
     
    */
    class Producer implements Runnable {    //通過接口Runnable實現多線程
        private Info info = null ;            //保存Info引用
        
        
    public Producer(Info info) {        //通過構造方法傳遞引用
            this.info = info ;
        }
        
        @Override
        
    public void run() {
            
    boolean flag = false ;                //定義標志位            
            for(int i = 0; i < 50; i++) {        //生產50次信息
                if(flag){    //如果標志位為true 將設置 中文內容
                    this.info.setName("小星") ;    //設置名字
                    try {
                        
    //為了更好的體現代碼運行效果在設置姓名和內容之間加入延遲操作
                        Thread.sleep(300) ;
                    } 
    catch (InterruptedException e) {        //線程被打斷后會拋出此異常
                        e.printStackTrace();
                    }            
                    
    this.info.setContent("JAVA初學者") ;        //設置內容
                    flag = false ;    //改變標志位,用于變換輸入內容
                    
                }
    else{    //如果標志位為false 將設置英文內容        
                    this.info.setName("Solitary") ;    //設置名字
                    try {
                        
    //為了更好的體現代碼運行效果在設置姓名和內容之間加入延遲操作
                        Thread.sleep(300) ;
                    } 
    catch (InterruptedException e) {        //線程被打斷后會拋出此異常
                        e.printStackTrace();
                    }            
                    
    this.info.setContent("Coder") ;        //設置內容
                    flag = true ;    //改變標志位,用于變換輸入內容
                }
            }
        }    
        
    } ;

    /**
     * 定義消費者
     * 
    @author Solitary
     
    */
    class Consumer implements Runnable {
        
    private Info info = null ;        //用于保存Info引用,其目的是為了讓消費者和生產者擁有同一個info
        
        
    public Consumer(Info info){
            
    this.info = info ;
        }
        
    public void run() {
            
    for(int i = 0; i < 50; i++) {        //消費和也從info中取50次消息
                try {
                    Thread.sleep(
    300) ;
                } 
    catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(
    this.info.getName() + 
                        
    " --> " + this.info.getContent()) ;
            }
        }
    } ;

    /**
     * 測試代碼
     * 
    @author Solitary\
     
    */
    public class MultiThreadingDemo01 {
        
    public static void main(String args[]){        
            Info info 
    = new Info() ;    // 實例化Info對象
            Producer pro = new Producer(info) ;        // 生產者
            Consumer con = new Consumer(info) ;        // 消費者
            new Thread(pro).start() ;        //啟動線程
            new Thread(con).start() ;        //啟動線程        
        }
    }
    示例一(執行效果):
    小星 --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    Solitary 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    Solitary 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> Coder
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    Solitary 
    --> JAVA初學者
    小星 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    Solitary 
    --> JAVA初學者
    Solitary 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    請注意運行結果,為什么我代碼里面明明成對設置的是:小星 --> JAVA初學者;  Solitary --> Coder而運行結果確實有不匹配的呢?
    分析:
    因為生產者和消費者的線程都已啟動,那么不能保證誰在前,或者誰在后,在生產者還在設置內容的時候(比如:已經設置好的Info的name=小星,Context=JAVA初學者,而此時生產者又設置了name = Solitary正打算設置Content = Coder),而消費者已經取走了內容,那么顯示的肯定就是Solitary --> JAVA初學者這樣的結果,因為兩個線程都在這執行著,出現了不匹配的結果。可以將代碼修改為:

    示例二

    package org.lx.multithreading;

    /**
     * 定義一個信息類
     * 
    @author Solitary
     
    */
    class Info {    
        
    private String name = "羅星" ;            //定義name屬性        
        private String content ="JAVA初學者";    //定義content屬性
        
        
    //getter     setter
        public String getName() {
            
    return name;
        }
        
    public void setName(String name) {
            
    this.name = name;
        }
        
    public String getContent() {
            
    return content;
        }
        
    public void setContent(String content) {
            
    this.content = content ;
        }
        
        
    public synchronized void set(String name, String content){        //由此方法統一設置信息
            this.setName(name) ;
            
    try {
                
    /* 此時這個地方加不加延遲沒有任何關系,因為該方法已經同步
                   在執行到此方法(get())時,此方法將會完整結束后才會執行
                   到別的方法 ,所以一定會完整設置完信息之后才會輪到信息的讀取方法
    */
                Thread.sleep(
    300) ;            
            } 
    catch (InterruptedException e) {
                e.printStackTrace();
            }
            
    this.setContent(content) ;
        }
        
        
    public synchronized void get(){
            
    try {
                Thread.sleep(
    300) ;
            } 
    catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(
    this.getName() + 
                    
    " --> " + this.getContent()) ;
        }
    } ;

    /**
     * 定義生產者
     * 
    @author Solitary
     
    */
    class Producer implements Runnable {    //通過接口Runnable實現多線程
        private Info info = null ;            //保存Info引用
        
        
    public Producer(Info info) {        //通過構造方法傳遞引用
            this.info = info ;
        }
        
        @Override
        
    public void run() {
            
    boolean flag = false ;                //定義標志位            
            for(int i = 0; i < 50; i++) {        //生產50次信息
                if(flag){    //如果標志位為true 將設置 中文內容
                    this.info.set("小星""JAVA初學者") ;
                    flag 
    = false ;    //改變標志位,用于變換輸入內容
                    
                }
    else{    //如果標志位為false 將設置英文內容        
                    this.info.set("Solitary""Coder") ;
                    flag 
    = true ;    //改變標志位,用于變換輸入內容
                }
            }
        }    
        
    } ;

    /**
     * 定義消費者
     * 
    @author Solitary
     
    */
    class Consumer implements Runnable {
        
    private Info info = null ;        //用于保存Info引用,其目的是為了讓消費者和生產者擁有同一個info
        
        
    public Consumer(Info info){
            
    this.info = info ;
        }
        
    public void run() {
            
    for(int i = 0; i < 50; i++) {        //消費和也從info中取50次消息
                this.info.get() ;
            }
        }
    } ;

    /**
     * 測試代碼
     * 
    @author Solitary\
     
    */
    public class MultiThreadingDemo01 {
        
    public static void main(String args[]){        
            Info info 
    = new Info() ;    // 實例化Info對象
            Producer pro = new Producer(info) ;        // 生產者
            Consumer con = new Consumer(info) ;        // 消費者
            new Thread(pro).start() ;        //啟動線程
            new Thread(con).start() ;        //啟動線程        
        }
    }
    示例二(運行效果)
    Solitary --> Coder
    Solitary 
    --> Coder
    Solitary 
    --> Coder
    Solitary 
    --> Coder
    Solitary 
    --> Coder
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    Solitary 
    --> Coder
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    小星 
    --> JAVA初學者
    觀察運行結果發現,不匹配的現象已經解決了,因為設置name 與 content 的步驟都在一個同步方法中,所以不會導致設置不完整的情況,但是并沒有達到我們想要的結果, 觀察出現了連續出現重復的內容,這是為什么呢? 分析:這兩個方法在不同的線程中,當一條線程中的方法設置完內容之后,另一個線程取出內容顯示,取完之后當了土財主不讓,繼續取,生產者無法更新里邊的內容,那顯示出來的內容肯定就是重復的,因為多線程中,不能保證這個線程什么時候執行。怎樣解決呢?

    1.中間那塊矩形代表信息載體Info實例,載體中沒有產品的時候上面顯示為綠燈,那么這時生產者能放入產品。而消費者不能取出產品。


    2.當生產者放入產品之后燈變成紅色,這時候生產者將不能放入產品,而輪到消費者取出產品,那么去完之后再次改變燈為綠色。這樣一直反復執行下去。

    那么載體上的那盞燈屬于一個標志位,我們可以在代碼中用boolean表示,那么這盞燈是屬于信息載體Info的標志,因為設置/取出這兩個方法都在Info中定義,生產者與消費者只是負責調用Info之中的方法,就讓Info中的方法判斷一下自身的標志位的狀態判斷是否生產或者取出。

    實例三
    package org.lx.multithreading ;

    class Info        //定義信息類
    {
        
    private String name = "羅星" ;        //定義name屬性
        private String content = "JAVA初學者" ;        //定義content屬性
        private boolean flag = false ;    //設置標志位


        
    public synchronized void set(String name, String content){
            
    if(!flag){        //方法每次執行的時候都檢查一下標志位狀態,從而判斷時候進行生產
                try{
                    
    super.wait() ;
                }
    catch(InterruptedException e){
                    e.printStackTrace() ;
                }
            }
            
    this.setName(name) ;        //設置名稱
            try{
                Thread.sleep(
    300) ;
            }
    catch(InterruptedException e){
                e.printStackTrace() ;
            }
            
    this.setContent(content) ;    // 設置內容
            flag = false ;        //改變標志位,表示可以取走
            super.notify() ;    //喚醒線程
        }

        
    public synchronized void get(){
            
    if(flag){        ////方法每次執行的時候都檢查一下標志位狀態,從而判斷時候進行取出
                try{
                    
    super.wait() ;
                }
    catch(InterruptedException e){
                    e.printStackTrace() ;
                }
            }
            
    try{
                Thread.sleep(
    300) ;
            }
    catch(InterruptedException e){
                e.printStackTrace() ;
            }
            System.out.println(
    this.getName() +
                    
    " --> " + this.getContent()) ;
            flag 
    = true ;    //改變標志位,表示可以生成    
            super.notify() ;
        }

        
    public void setName(String name){
            
    this.name = name ;
        }

        
    public String getName(){
            
    return this.name ;
        }

        
    public void setContent(String content){
            
    this.content = content ;
        }

        
    public String getContent(){
            
    return this.content ;
        }
    }

    class Producer implements Runnable        //通過Runnable實現多線程
    {
        
    public Info info = null ;        //保存Info引用
        public Producer(Info info){
            
    this.info = info ;
        }

        
    public void run(){
            
    boolean flag = false ;    //定義標記位
            for(int i = 0; i < 50; i++){
                
    if(flag){
                    
    this.info.set("羅星""JAVA初學者") ;
                    flag 
    = false ;
                }
    else{
                    
    this.info.set("Solitary""Coder") ;
                    flag 
    = true ;
                }
            }
        }
    } ;

    class Consumer implements Runnable{        //消費者類
        private Info info = null ;        
        
    public Consumer(Info info){
            
    this.info = info ;
        }

        
    public void run(){
            
    for(int i = 0; i < 50; i++){
                
    this.info.get() ;
            }
        }
    } ;

    //測試代碼
    public class MultiThreading
    {
        
    public static void main(String[] args){
            Info info 
    = new Info() ;    //實例化Info對象
            Producer pro = new Producer(info) ;    //生產者
            Consumer con = new Consumer(info) ;    //消費者
            new Thread(pro).start() ;
            
    new Thread(con).start() ;
        }
    }
    在此利用了Object類對線程的支持,Object中定義了方法:
    public final void wait() throws InterruptedException
    在其他線程調用此對象的 notify() 方法或 notifyAll() 方法前,導致當前線程等待。
    換句話說,此方法的行為就好像它僅執行 wait(0) 調用一樣。

    當前線程必須擁有此對象監視器。該線程發布對此監視器的所有權并等待,直到其他線程通過調用 notify 方法,或 notifyAll 方法通知在此對象的監視器上等待的線程醒來。然后該線程將等到重新獲得對監視器的所有權后才能繼續執行。 

    public final void notify()
    喚醒在此對象監視器上等待的單個線程。如果所有線程都在此對象上等待,則會選擇喚醒其中一個線程。選擇是任意性的,并在對實現做出決定時發生。線程通過調用其中一個 wait 方法,在對象的監視器上等待。

    直到當前線程放棄此對象上的鎖定,才能繼續執行被喚醒的線程。被喚醒的線程將以常規方式與在該對象上主動同步的其他所有線程進行競爭;例如,喚醒的線程在作為鎖定此對象的下一個線程方面沒有可靠的特權或劣勢。  

    當生產者線程調用此方法時,首先檢查一下標志位,判斷是否等待,此時消費者也在調用相應方法判斷是否取出,如果可以取出,那么取出后改變標志位的狀態,然后喚醒該對象中等待的線程,這時狀態改變生產者既進行生產操作。


    序言:
    小弟是一個JAVA新手,這也是第一次寫博客,此文純屬于學習筆記,以便日后復習,如果前輩們認為描述有錯誤的地方,或者覺得不清晰感覺思路混亂的地方,還請前輩們多多賜教,晚生感激不勝! 謝謝。

    posted on 2011-10-29 18:26 Solitary 閱讀(2309) 評論(1)  編輯  收藏

    評論

    # re: 生產者與消費者(多線程經典案例) 2014-11-11 21:53 李興華

    親,你這明顯是抄襲我的嘛,我是魔樂科技的李興華;
    希望你能改過自新  回復  更多評論   


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    <2014年11月>
    2627282930311
    2345678
    9101112131415
    16171819202122
    23242526272829
    30123456

    導航

    統計

    常用鏈接

    留言簿(1)

    隨筆分類

    隨筆檔案

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲日本一区二区一本一道| 免费看内射乌克兰女| 中文字幕不卡亚洲| 夫妻免费无码V看片| 最近中文字幕完整免费视频ww| 男人扒开添女人下部免费视频| 亚洲不卡在线观看| 亚洲AV日韩AV高潮无码专区| 久久精品国产亚洲精品| 在线观看亚洲免费| 免费无码黄十八禁网站在线观看| 久久久久国产免费| 国产猛男猛女超爽免费视频| 有色视频在线观看免费高清在线直播 | 中文字幕亚洲一区| 免费一级做a爰片性色毛片| 免费观看黄网站在线播放| 国产91色综合久久免费分享| 全部免费毛片在线播放| 国产免费一区二区三区在线观看| 国产无遮挡色视频免费观看性色| 一区二区三区视频免费| 男女猛烈激情xx00免费视频| 污视频网站在线观看免费| 麻豆91免费视频| 国产精品亚洲а∨无码播放麻豆| 亚洲av无码兔费综合| 亚洲丁香婷婷综合久久| 亚洲男人的天堂网站| 亚洲欧美成人av在线观看| 亚洲欧美自偷自拍另类视| 亚洲国产精品综合久久20| 亚洲AV成人噜噜无码网站| 亚洲香蕉久久一区二区| 丁香婷婷亚洲六月综合色| 亚洲熟女精品中文字幕| 亚洲第一成年免费网站| 日日摸夜夜添夜夜免费视频| 一级毛片一级毛片免费毛片| selaoban在线视频免费精品| 两性色午夜免费视频|