1. 程式人生 > >Java多執行緒實現非同步呼叫

Java多執行緒實現非同步呼叫

在JAVA平臺,實現非同步呼叫的角色有如下三個角色:呼叫者 提貨單   真實資料 一個呼叫者在呼叫耗時操作,不能立即返回資料時,先返回一個提貨單.然後在過一斷時間後憑提貨單來獲取真正的資料. 去蛋糕店買蛋糕,不需要等蛋糕做出來(假設現做要很長時間),只需要領個提貨單就可以了(去幹別的事情),等到蛋糕做好了,再拿提貨單取蛋糕就可以了。 publicclass Main {   
  1. publicstaticvoid main(String[] args) {   
  2.         System.out.println("main BEGIN");   
  3.         Host host = new Host();   
  4.         Data data1 = host.request(10'A');   
  5.         Data data2 = host.request(20'B');   
  6.         Data data3 = host.request(30'C');   
  7.         System.out.println(
    "main otherJob BEGIN");   
  8. try {   
  9.             Thread.sleep(200);   
  10.         } catch (InterruptedException e) {   
  11.         }   
  12.         System.out.println("main otherJob END");   
  13.         System.out.println("data1 = " + data1.getContent());   
  14.         System.out.println("data2 = " + data2.getContent());   
  15.         System.out.println("data3 = " + data3.getContent());   
  16.         System.out.println("main END");   
  17.     }   
  18. }  
 這裡的main類就相當於“顧客”,host就相當於“蛋糕店”,顧客向“蛋糕店”定蛋糕就相當於“發請求request”,返回的資料data是FutureData的例項,就相當於提貨單,而不是真正的“蛋糕”。在過一段時間後(sleep一段時間後),呼叫data1.getContent(),也就是拿提貨單獲取執行結果。

下面來看一下,顧客定蛋糕後,蛋糕店做了什麼:

  1. publicclass Host {   
  2. public Data request(finalint count, finalchar c) {   
  3.         System.out.println("request(" + count + ", " + c + ") BEGIN");   
  4. // (1) 建立FutureData的實體
  5. final FutureData future = new FutureData();   
  6. // (2) 為了建立RealData的實體,啟動新的執行緒
  7. new Thread() {                                         
  8. publicvoid run() {   
  9.              //在匿名內部類中使用count、future、c。                        
  10.                 RealData realdata = new RealData(count, c);   
  11.                 future.setRealData(realdata);   
  12.             }                                                  
  13.         }.start();                                             
  14.         System.out.println("request(" + count + ", " + c + ") END");   
  15. // (3) 取回FutureData實體,作為傳回值
  16. return future;   
  17.     }   
  18. }  
  host("蛋糕店")在接到請求後,先生成了“提貨單”FutureData的例項future,然後命令“蛋糕師傅”RealData去做蛋糕,realdata相當於起個執行緒去做蛋糕了。然後host返回給顧客的僅僅是“提貨單”future,而不是蛋糕。當蛋糕做好後,蛋糕師傅才能給對應的“提貨單”蛋糕,也就是future.setRealData(realdata)。

下面來看看蛋糕師傅是怎麼做蛋糕的:

建立一個字串,包含count個c字元,為了表現出犯法需要花費一些時間,使用了sleep。

publicclass RealData implements Data {   
  1. privatefinal String content;   
  2. public RealData(int count, char c) {   
  3.         System.out.println("making RealData(" + count + ", " + c + ") BEGIN");   
  4. char[] buffer = newchar[count];   
  5. for (int i = 0; i < count; i++) {   
  6.             buffer[i] = c;   
  7. try {   
  8.                 Thread.sleep(1000);   
  9.             } catch (InterruptedException e) {   
  10.             }   
  11.         }   
  12.         System.out.println("making RealData(" + count + ", " + c + ") END");   
  13. this.content = new String(buffer);   
  14.     }   
  15. public String getContent() {   
  16. return content;   
  17.     }   
  18. }  

     現在來看看“提貨單”future是怎麼與蛋糕"content"對應的:

publicclass FutureData implements Data {   
  1. private RealData realdata = null;   
  2. privateboolean ready = false;   
  3. publicsynchronizedvoid setRealData(RealData realdata) {   
  4. if (ready) {                           
  5. return;     // 防止setRealData被呼叫兩次以上。
  6.         }   
  7. this.realdata = realdata;   
  8. this.ready = true;   
  9.         notifyAll();   
  10.     }   
  11. publicsynchronized String getContent() {   
  12. while (!ready) {   
  13. try {   
  14.                 wait();   
  15.             } catch (InterruptedException e) {   
  16.             }   
  17.         }   
  18. return realdata.getContent();   
  19.     }   
  20. }  

   顧客做完自己的事情後,會拿著自己的“提貨單”來取蛋糕:

System.out.println("data1 = " + data1.getContent());  

 這時候如果蛋糕沒做好,就只好等了:

while (!ready) {   
  1. try {   
  2.                 wait();   
  3.             } catch (InterruptedException e) {   
  4.             }   
  5. //等做好後才能取到  
  6. return realdata.getContent();  

   程式分析

   對於每個請求,host都會生成一個執行緒,這個執行緒負責生成顧客需要的“蛋糕”。在等待一段時間以後,如果蛋糕還沒有做好,顧客還必須等待。直到“蛋糕被做好”,也就是

future.setRealData(realdata); 執行以後,顧客才能拿走蛋糕。

   每個執行緒只是專門負責製作特定顧客所需要的“蛋糕”。也就是顧客A對應著蛋糕師傅A,顧客B對應著蛋糕師傅B。即使顧客B的蛋糕被先做好了,顧客A也只能等待蛋糕師傅A把蛋糕做好。換句話說,顧客之間沒有競爭關係。

   類FutureData的兩個方法被設定為synchronized,實際上蛋糕師傅A與顧客A之間的互斥關係,也就是顧客A必須等待蛋糕師傅A把蛋糕做好後,才能拿走,而與蛋糕師傅B是否做好了蛋糕沒有關係。