多線程操作實例——生產者與消費者


面對多線程學習生產者與消費者是最基本的實例

對於java后端開發的人員必須要掌握,還有考研考試計算機操作系統的同鞋。

下面是三個實例對於生產者與消費者的的例子,層層遞進,逐步解決問題。

問題:生產者——設置信息名字name,和內容content

        消費者——負責取出設置的信息。

一、基本實現

由於線程的不確定性可能出現以下問題:

(1)消費者取出的信息不匹配,即不是由同一個生產者設置的信息

(2)生產者生產了多個信息,消費者才開始取出信息,或消費者取出的重復的信息。

上面的問題下面會逐一解決,下面先看出現問題的程序:

package li.xin.hua.ch9;
/*線程生產者與向消費者最基本實現,問題有:
 * 1、數據不匹配
 * 2、數據重復取出已經取過的數據*/
class Info{
    private String name;
    private String content;
    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;
    }
};
class Producer implements Runnable{
    private Info info=null;
    public Producer(Info info){
        this.info=info;
    }
    public void run(){
        boolean flag=false;
        for(int i=0;i<10;++i)
        {
          if(flag){
              this.info.setName("胡歌");
              try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
              this.info.setContent("林殊");
              flag=false;
          }else{
              this.info.setName("劉濤");
              try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
              this.info.setContent("郡主");
              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<10;i++)
        {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    System.out.println(this.info.getName()+"---飾演--->"+this.info.getContent());
        }
    }
};
public class Producer_Comsumer01 {
public static void main(String[] args) {
  Info info=new Info();
  Producer pro=new Producer(info);
  Consumer con=new Consumer(info);
  new Thread(pro).start();
  new Thread(con).start();
  
}
}

運行結果如下圖:

發現胡歌不僅飾演林殊,還飾演郡主,哈哈哈哈哈哈!

問題是線程生產的信息取出時是不匹配的,解決方法使用同步機制——synchronized

二、加入同步機制

將設置名稱與內容定義在一個同步方法中,代碼如下:

package li.xin.hua.ch9;
/*線程生產者與向消費者最基本實現
 * 1、數據不匹配通過同步機制已經解決
 * 2、重復取數據問題還是有*/
class Info02{
    private String name;
    private String content;
    public synchronized void get() {
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(this.name+"---飾演--->"+this.content);
    }
    
    public synchronized void set(String name,String content) {
        this.name=name;
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.content = content;
    }
};
class Producer02 implements Runnable{
    private Info02 info=null;
    public Producer02(Info02 info){
        this.info=info;
    }
    public void run(){
        boolean flag=false;
        for(int i=0;i<10;++i)
        {
          if(flag){
              this.info.set("胡歌","林殊");
              
              flag=false;
          }else{
              this.info.set("劉濤","郡主");
              
              flag=true;
          }
        }
    }
};
class Consumer02 implements Runnable{
    private Info02 info=null;
    public Consumer02(Info02 info){
        this.info=info;
    }
    public void run(){
        for(int i=0;i<10;i++)
        {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.info.get();
        }
    }
};
public class Producer_Comsumer02 {
public static void main(String[] args) {
  Info02 info=new Info02();
  Producer02 pro=new Producer02(info);
  Consumer02 con=new Consumer02(info);
  new Thread(pro).start();
  new Thread(con).start();
  
}
}

運行結果如下圖:

胡歌與劉濤飾演的角色沒有匹配錯誤,但信息反復取出,需要Object類中的方法來解決。

三、加入等待喚醒機制

Object類中wait()、notify()方法,擴充點知識:wai()方法會釋放線程的對象的鎖,而sleep()方法不會。

設置一個標志位flag,

當flag為true時:

     可以進行生產,但不能取出數據,若此時消費者線程恰巧搶到CPU資源,想要執行消費者程序,

    必須將消費者線程等待wait()。生產者生產完成后要修改標示位(表示可以消費者可以取出信息了),和喚醒notify()被等待的線程。

當flag為false時:

    消費者可以取出信息,但生產者不能生產信息,若此時生產者線程恰巧搶到CPU資源,想要執行生產者程序,

    必須將生產者線程等待wait()。消費者完成取出信息后要修改標示位(表示可以生產者可以生產信息了),和喚醒notify()被等待的線程。

 

package li.xin.hua.ch9;
/*線程生產者與向消費者最基本實現
 * 1、數據不匹配通過同步機制已經解決
 * 2、重復取數據問題通過等待喚醒機制已經解決
 * 當flag為true時允許生產者生產,若此時消費者進入則要等待
 * 當flag為false時允許消費者取出信息,若此時生產者進入則要等待*/
class Info03{
    private String name;
    private String content;
    private boolean flag=true; /* 設置標示位:true是生產的時間,false是消費的時間。
                                                                             第一次先生產*/
    public synchronized void set(String name,String content) {
        if(!flag) /*現在不是生產的時間,線程要等待,喚醒后才能生產。*/
        {
            try {
                super.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        this.name=name;
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.content = content;
        flag=false;
        super.notify();
    }
    
    public synchronized void get() {
        if(flag) /*消費者*/
        {
            try {
                super.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
            
        System.out.println(this.name+"---飾演--->"+this.content);
        flag=true;
        super.notify();
    }
};
class Producer03 implements Runnable{
    private Info03 info=null;
    public Producer03(Info03 info){
        this.info=info;
    }
    public void run(){
        boolean flag=false;
        for(int i=0;i<10;++i)
        {
          if(flag){
              this.info.set("胡歌","林殊");
              
              flag=false;
          }else{
              this.info.set("劉濤","郡主");
              
              flag=true;
          }
        }
    }
};
class Consumer03 implements Runnable{
    private Info03 info=null;
    public Consumer03(Info03 info){
        this.info=info;
    }
    public void run(){
        for(int i=0;i<10;i++)
        {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.info.get();
        }
    }
};
public class Producer_Comsumer03 {
public static void main(String[] args) {
  Info03 info=new Info03();
  Producer03 pro=new Producer03(info);
  Consumer03 con=new Consumer03(info);
  new Thread(pro).start();
  new Thread(con).start();
  
}
}

運行結果如下圖:

胡歌與劉濤交替出現,並且角色匹配正確。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM