標籤:
------<a href="http://www.itheima.com" target="blank">Java培訓、Android培訓、iOS培訓、.Net培訓</a>、期待與您交流! -------
多線程可以讓我們同時共用一個資源,但如果在共用這個資源時需要彼此之間的聯絡怎麼做呢?
經典執行個體:生產者與消費者。
問題描述,生產者每生產一個消費者就要取走一個,同時進行。
首先java為我們提供了一套等待喚醒機制,讓線程與線程之間產生了聯絡。線程是分五個狀態的:建立;運行;阻塞;凍結;消亡。java提供了幾個針對狀態的方法
wait()方法讓線程進入凍結狀態,讓出cpu,讓出鎖;
notify()方法喚醒進入凍結狀態的線程,notifyAll()是喚醒所有的線程。
針對問題,我們可以對生產者設定一個開關,如果資源為0就允許其生產並在生產完之後將開關關上,不會在有生產者生產並喚醒其他線程也就是消費者線程,當然在這之上同樣要用到同步,為了保證第一個生產者進去之時,其他生產者會被拒之門外,對於消費者同理。
public static void main(String[] args) {// tongbu i=new tongbu();//建立抽取出來的同步方法對象hjw j=new hjw(i);//建立實現Runnable介面的對象bb k=new bb(i);Thread xc1 =new Thread(j);//建立線程Thread xc2 =new Thread(k);xc1.setName("生產者");xc2.setName("消費者");xc1.start();xc2.start();}}class tongbu{//將兩個同步方法抽取出來private String name;private int count=1;boolean flag=false;//定義一個bool類型來作用等待喚醒機制public synchronized void set(String name){//生產者的同步函數if(flag){//已存在就睡眠try{wait();}catch(Exception e){}}//否則 運作代碼this.name=name+"---"+count++;System.out.println(Thread.currentThread().getName()+this.name);flag=true;//改變bool值this.notify();//喚醒線程}public synchronized void get(){if(!flag){//進入休眠try{wait();}catch(Exception e){}}//被生產者喚醒System.out.println(Thread.currentThread().getName()+"---"+this.name);flag=false;this.notify();//喚醒線程}}class hjw implements Runnable{private tongbu x ;hjw(tongbu x){//傳回tongbu對象this.x=x;}public void run(){//重寫run方法while(true){x.set("餅乾");}}}class bb implements Runnable{private tongbu x ;bb(tongbu x){//傳回tongbu對象this.x=x;}public void run(){//重寫run方法while(true){x.get();}}}
這隻是對於生產者和消費者只有一個線程,如果這邊都有多個線程呢?絕不是多建立幾個線程那麼簡單,因為要notify方法是喚醒最先休眠的那個線程,也就是說轉到後面會出,生產一個,消費兩次,生產多個只消費其中一個。
那麼如何避免呢?採用while迴圈替代if迴圈,每當線程解凍之後,重新開始迴圈而非接著向下執行,這樣就避免了多次執行,但問題是依據notify()的特性還是會讓所有線程都陷入等待,我需要喚醒對方線程,所有這個時候就要用到notifyAll()了,喚醒所有線程。
public static void main(String[] args) {// tongbu i=new tongbu();//建立抽取出來的同步方法對象hjw j=new hjw(i);//建立實現Runnable介面的對象bb k=new bb(i);Thread xc1 =new Thread(j);//建立線程Thread xc2 =new Thread(k);xc1.setName("生產者");xc2.setName("消費者");xc1.start();xc2.start();}}class tongbu{//將兩個同步方法抽取出來private String name;private int count=1;boolean flag=false;//定義一個bool類型來作用等待喚醒機制public synchronized void set(String name){//生產者的同步函數while(flag){//已存在就睡眠try{wait();}catch(Exception e){}}//否則 運作代碼this.name=name+"---"+count++;System.out.println(Thread.currentThread().getName()+this.name);flag=true;//改變bool值this.notifyAll();//喚醒線程}public synchronized void get(){while(!flag){//進入休眠try{wait();}catch(Exception e){}}//被生產者喚醒System.out.println(Thread.currentThread().getName()+"---"+this.name);flag=false;this.notifyAll();//喚醒線程}}class hjw implements Runnable{private tongbu x ;hjw(tongbu x){//傳回tongbu對象this.x=x;}public void run(){//重寫run方法while(true){x.set("餅乾");}}}class bb implements Runnable{private tongbu x ;bb(tongbu x){//傳回tongbu對象this.x=x;}public void run(){//重寫run方法while(true){x.get();}}}
事實上,java5.0版本提供更完善的解決方案,針對如何喚醒指定線程。在java.lang.util.concurrent.locks中,提供了方法將鎖顯示化用以取代同步synchronized,lock()開鎖,unlock()關鎖,關鎖這一特點猶如關閉資源就是無論怎樣都要執行,因為前面有異常需要處理,所以關鎖這一操作放在了finally{}中;而對於Object中wait()方法和notify(),notifyAll()方法分別用condition的await(),signal()和signalAll()取代,在lock中有一個new condition的方法,所以可以通過建立多個condition類,實現對指定線程的操作。
黑馬程式員————java線程之間的通訊