面對多線程學習生產者與消費者是最基本的實例
對於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(); } }
運行結果如下圖:
胡歌與劉濤交替出現,並且角色匹配正確。