生產(chǎn)者與消費(fèi)者(多線程經(jīng)典案例)
注:此示例來自MLDN講師李興華JAVA SE基礎(chǔ)教學(xué)部分生產(chǎn)者和消費(fèi)者是多線程中一個(gè)經(jīng)典的操作案例下面一起看下代碼:
示例一:
package org.lx.multithreading;
/**
* 定義一個(gè)信息類
* @author Solitary
*/
class Info {
private String name = "羅星" ; //定義name屬性
private String content ="JAVA初學(xué)者"; //定義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;
}
} ;
/**
* 定義生產(chǎn)者
* @author Solitary
*/
class Producer implements Runnable { //通過接口Runnable實(shí)現(xiàn)多線程
private Info info = null ; //保存Info引用
public Producer(Info info) { //通過構(gòu)造方法傳遞引用
this.info = info ;
}
@Override
public void run() {
boolean flag = false ; //定義標(biāo)志位
for(int i = 0; i < 50; i++) { //生產(chǎn)50次信息
if(flag){ //如果標(biāo)志位為true 將設(shè)置 中文內(nèi)容
this.info.setName("小星") ; //設(shè)置名字
try {
//為了更好的體現(xiàn)代碼運(yùn)行效果在設(shè)置姓名和內(nèi)容之間加入延遲操作
Thread.sleep(300) ;
} catch (InterruptedException e) { //線程被打斷后會(huì)拋出此異常
e.printStackTrace();
}
this.info.setContent("JAVA初學(xué)者") ; //設(shè)置內(nèi)容
flag = false ; //改變標(biāo)志位,用于變換輸入內(nèi)容
}else{ //如果標(biāo)志位為false 將設(shè)置英文內(nèi)容
this.info.setName("Solitary") ; //設(shè)置名字
try {
//為了更好的體現(xiàn)代碼運(yùn)行效果在設(shè)置姓名和內(nèi)容之間加入延遲操作
Thread.sleep(300) ;
} catch (InterruptedException e) { //線程被打斷后會(huì)拋出此異常
e.printStackTrace();
}
this.info.setContent("Coder") ; //設(shè)置內(nèi)容
flag = true ; //改變標(biāo)志位,用于變換輸入內(nèi)容
}
}
}
} ;
/**
* 定義消費(fèi)者
* @author Solitary
*/
class Consumer implements Runnable {
private Info info = null ; //用于保存Info引用,其目的是為了讓消費(fèi)者和生產(chǎn)者擁有同一個(gè)info
public Consumer(Info info){
this.info = info ;
}
public void run() {
for(int i = 0; i < 50; i++) { //消費(fèi)和也從info中取50次消息
try {
Thread.sleep(300) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.info.getName() +
" --> " + this.info.getContent()) ;
}
}
} ;
/**
* 測(cè)試代碼
* @author Solitary\
*/
public class MultiThreadingDemo01 {
public static void main(String args[]){
Info info = new Info() ; // 實(shí)例化Info對(duì)象
Producer pro = new Producer(info) ; // 生產(chǎn)者
Consumer con = new Consumer(info) ; // 消費(fèi)者
new Thread(pro).start() ; //啟動(dòng)線程
new Thread(con).start() ; //啟動(dòng)線程
}
}
小星 --> JAVA初學(xué)者
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> Coder
Solitary --> JAVA初學(xué)者
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
Solitary --> Coder
Solitary --> JAVA初學(xué)者
Solitary --> Coder
小星 --> Coder
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> Coder
Solitary --> JAVA初學(xué)者
小星 --> Coder
小星 --> JAVA初學(xué)者
Solitary --> Coder
Solitary --> JAVA初學(xué)者
Solitary --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> Coder
小星 --> JAVA初學(xué)者
Solitary --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
分析:
因?yàn)樯a(chǎn)者和消費(fèi)者的線程都已啟動(dòng),那么不能保證誰在前,或者誰在后,在生產(chǎn)者還在設(shè)置內(nèi)容的時(shí)候(比如:已經(jīng)設(shè)置好的Info的name=小星,Context=JAVA初學(xué)者,而此時(shí)生產(chǎn)者又設(shè)置了name = Solitary正打算設(shè)置Content = Coder),而消費(fèi)者已經(jīng)取走了內(nèi)容,那么顯示的肯定就是Solitary --> JAVA初學(xué)者這樣的結(jié)果,因?yàn)閮蓚€(gè)線程都在這執(zhí)行著,出現(xiàn)了不匹配的結(jié)果。可以將代碼修改為:
示例二
/**
* 定義一個(gè)信息類
* @author Solitary
*/
class Info {
private String name = "羅星" ; //定義name屬性
private String content ="JAVA初學(xué)者"; //定義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){ //由此方法統(tǒng)一設(shè)置信息
this.setName(name) ;
try {
/* 此時(shí)這個(gè)地方加不加延遲沒有任何關(guān)系,因?yàn)樵摲椒ㄒ呀?jīng)同步
在執(zhí)行到此方法(get())時(shí),此方法將會(huì)完整結(jié)束后才會(huì)執(zhí)行
到別的方法 ,所以一定會(huì)完整設(shè)置完信息之后才會(huì)輪到信息的讀取方法*/
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()) ;
}
} ;
/**
* 定義生產(chǎn)者
* @author Solitary
*/
class Producer implements Runnable { //通過接口Runnable實(shí)現(xiàn)多線程
private Info info = null ; //保存Info引用
public Producer(Info info) { //通過構(gòu)造方法傳遞引用
this.info = info ;
}
@Override
public void run() {
boolean flag = false ; //定義標(biāo)志位
for(int i = 0; i < 50; i++) { //生產(chǎn)50次信息
if(flag){ //如果標(biāo)志位為true 將設(shè)置 中文內(nèi)容
this.info.set("小星", "JAVA初學(xué)者") ;
flag = false ; //改變標(biāo)志位,用于變換輸入內(nèi)容
}else{ //如果標(biāo)志位為false 將設(shè)置英文內(nèi)容
this.info.set("Solitary", "Coder") ;
flag = true ; //改變標(biāo)志位,用于變換輸入內(nèi)容
}
}
}
} ;
/**
* 定義消費(fèi)者
* @author Solitary
*/
class Consumer implements Runnable {
private Info info = null ; //用于保存Info引用,其目的是為了讓消費(fèi)者和生產(chǎn)者擁有同一個(gè)info
public Consumer(Info info){
this.info = info ;
}
public void run() {
for(int i = 0; i < 50; i++) { //消費(fèi)和也從info中取50次消息
this.info.get() ;
}
}
} ;
/**
* 測(cè)試代碼
* @author Solitary\
*/
public class MultiThreadingDemo01 {
public static void main(String args[]){
Info info = new Info() ; // 實(shí)例化Info對(duì)象
Producer pro = new Producer(info) ; // 生產(chǎn)者
Consumer con = new Consumer(info) ; // 消費(fèi)者
new Thread(pro).start() ; //啟動(dòng)線程
new Thread(con).start() ; //啟動(dòng)線程
}
}
Solitary --> Coder
Solitary --> Coder
Solitary --> Coder
Solitary --> Coder
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> Coder
小星 --> JAVA初學(xué)者
Solitary --> Coder
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
小星 --> JAVA初學(xué)者
題.png)
1.中間那塊矩形代表信息載體Info實(shí)例,載體中沒有產(chǎn)品的時(shí)候上面顯示為綠燈,那么這時(shí)生產(chǎn)者能放入產(chǎn)品。而消費(fèi)者不能取出產(chǎn)品。

2.當(dāng)生產(chǎn)者放入產(chǎn)品之后燈變成紅色,這時(shí)候生產(chǎn)者將不能放入產(chǎn)品,而輪到消費(fèi)者取出產(chǎn)品,那么去完之后再次改變燈為綠色。這樣一直反復(fù)執(zhí)行下去。
那么載體上的那盞燈屬于一個(gè)標(biāo)志位,我們可以在代碼中用boolean表示,那么這盞燈是屬于信息載體Info的標(biāo)志,因?yàn)樵O(shè)置/取出這兩個(gè)方法都在Info中定義,生產(chǎn)者與消費(fèi)者只是負(fù)責(zé)調(diào)用Info之中的方法,就讓Info中的方法判斷一下自身的標(biāo)志位的狀態(tài)判斷是否生產(chǎn)或者取出。
實(shí)例三
class Info //定義信息類
{
private String name = "羅星" ; //定義name屬性
private String content = "JAVA初學(xué)者" ; //定義content屬性
private boolean flag = false ; //設(shè)置標(biāo)志位
public synchronized void set(String name, String content){
if(!flag){ //方法每次執(zhí)行的時(shí)候都檢查一下標(biāo)志位狀態(tài),從而判斷時(shí)候進(jìn)行生產(chǎn)
try{
super.wait() ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
}
this.setName(name) ; //設(shè)置名稱
try{
Thread.sleep(300) ;
}catch(InterruptedException e){
e.printStackTrace() ;
}
this.setContent(content) ; // 設(shè)置內(nèi)容
flag = false ; //改變標(biāo)志位,表示可以取走
super.notify() ; //喚醒線程
}
public synchronized void get(){
if(flag){ ////方法每次執(zhí)行的時(shí)候都檢查一下標(biāo)志位狀態(tài),從而判斷時(shí)候進(jìn)行取出
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 ; //改變標(biāo)志位,表示可以生成
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實(shí)現(xiàn)多線程
{
public Info info = null ; //保存Info引用
public Producer(Info info){
this.info = info ;
}
public void run(){
boolean flag = false ; //定義標(biāo)記位
for(int i = 0; i < 50; i++){
if(flag){
this.info.set("羅星", "JAVA初學(xué)者") ;
flag = false ;
}else{
this.info.set("Solitary", "Coder") ;
flag = true ;
}
}
}
} ;
class Consumer implements Runnable{ //消費(fèi)者類
private Info info = null ;
public Consumer(Info info){
this.info = info ;
}
public void run(){
for(int i = 0; i < 50; i++){
this.info.get() ;
}
}
} ;
//測(cè)試代碼
public class MultiThreading
{
public static void main(String[] args){
Info info = new Info() ; //實(shí)例化Info對(duì)象
Producer pro = new Producer(info) ; //生產(chǎn)者
Consumer con = new Consumer(info) ; //消費(fèi)者
new Thread(pro).start() ;
new Thread(con).start() ;
}
}
notify()
方法或 notifyAll()
方法前,導(dǎo)致當(dāng)前線程等待。當(dāng)前線程必須擁有此對(duì)象監(jiān)視器。該線程發(fā)布對(duì)此監(jiān)視器的所有權(quán)并等待,直到其他線程通過調(diào)用 notify
方法,或 notifyAll
方法通知在此對(duì)象的監(jiān)視器上等待的線程醒來。然后該線程將等到重新獲得對(duì)監(jiān)視器的所有權(quán)后才能繼續(xù)執(zhí)行。
public final void notify()
- 喚醒在此對(duì)象監(jiān)視器上等待的單個(gè)線程。如果所有線程都在此對(duì)象上等待,則會(huì)選擇喚醒其中一個(gè)線程。選擇是任意性的,并在對(duì)實(shí)現(xiàn)做出決定時(shí)發(fā)生。線程通過調(diào)用其中一個(gè)
wait
方法,在對(duì)象的監(jiān)視器上等待。直到當(dāng)前線程放棄此對(duì)象上的鎖定,才能繼續(xù)執(zhí)行被喚醒的線程。被喚醒的線程將以常規(guī)方式與在該對(duì)象上主動(dòng)同步的其他所有線程進(jìn)行競(jìng)爭(zhēng);例如,喚醒的線程在作為鎖定此對(duì)象的下一個(gè)線程方面沒有可靠的特權(quán)或劣勢(shì)。
當(dāng)生產(chǎn)者線程調(diào)用此方法時(shí),首先檢查一下標(biāo)志位,判斷是否等待,此時(shí)消費(fèi)者也在調(diào)用相應(yīng)方法判斷是否取出,如果可以取出,那么取出后改變標(biāo)志位的狀態(tài),然后喚醒該對(duì)象中等待的線程,這時(shí)狀態(tài)改變生產(chǎn)者既進(jìn)行生產(chǎn)操作。
序言:
小弟是一個(gè)JAVA新手,這也是第一次寫博客,此文純屬于學(xué)習(xí)筆記,以便日后復(fù)習(xí),如果前輩們認(rèn)為描述有錯(cuò)誤的地方,或者覺得不清晰感覺思路混亂的地方,還請(qǐng)前輩們多多賜教,晚生感激不勝! 謝謝。
posted on 2011-10-29 18:26 Solitary 閱讀(2314) 評(píng)論(1) 編輯 收藏