1. 程式人生 > >由迅雷筆試題想到的——Guarded Suspension Pattern設計模式

由迅雷筆試題想到的——Guarded Suspension Pattern設計模式

有個網友,發了個帖子其中說到了迅雷的筆試題。帖子地址如下:

其中有道關於多執行緒的題目,覺得挺好玩,拿來分析一下:

有三個執行緒ID分別是A、B、C,請有多線程式設計實現,在螢幕上迴圈列印10次ABCABC…

題目分析:

三個執行緒,交替列印ABC,10次。所以可能的考點是

A.三個執行緒順序執行。

B.10次說明至少有一個計數器,初步假設可能為每個執行緒都有一個計數器(個人認為這樣子不可取)。另外一種假設是有公共區域,即關鍵區,題目意在考對關鍵區的讀寫操作。

粗略的看一下,感覺和Guarded Suspension Pattern設計模式很像。

【補充】舉例介紹一下Guarded Suspension Pattern模式。

【例子】你在家裡換衣服,忽然門鈴響了,原來是郵遞員送信來了。可是你衣服換到一半,不能開門,所以就說“請等我一下,我馬上來。”,郵遞員不敢闖入,只能等待在外。當你換號衣服,再開啟門,說句“不好意思,久等了。。。”,順利取到郵件。

guarded的意思是“被保護著,被防備著”,suspension的意思是“暫停”的意思。當現在不適合馬上執行某個操作時,就要求執行該操作的執行緒等候,以保證例項的安全性(在例子中就如同你的隱私權一樣)。這就是Guarded Suspension Pattern的核心思想。

這種設計模式還有其他的一些名稱,如 "guardard wait"或"spin wait"等。

對於本題,我設定了三個類。

PrintThread                 

ID非別為A、B、C的三個執行緒就是他了。通過建構函式傳入的name的不同,區分不同的執行緒。

RequestQueue            

三個執行緒請求的公共區域。RequestQueue會儲存三個執行緒要列印的資訊和列印次數。通過對printMsg方法的同步限制,從而使三個併發執行緒安全,正確的訪問關鍵區,即RequestQueue物件。

TestABC                       

Main函式所在的類,用來新建一個RequestQueue和三個執行緒類。

  1. //PrintThread 的程式碼
  2. package com.test.thread;  
  3. /** 
  4.  * 題目: 
  5.  * 有三個執行緒ID分別是A、B、C,請有多線程式設計實現,在螢幕上迴圈列印10次ABCABC… 
  6.  * 分析: 
  7.  * 1.三個執行緒,順序列印ABC,ABC...因此一定是需要判斷控制權的問題。 
  8.  * 2.列印十次,所以每個執行緒應該都有自己的計數器或三個執行緒公用一個計數器。 
  9.  * 3.可能相關的執行緒的設計模式:Guarded Suspension Pattern。  
  10.  * 結論: 
  11.  * 綜上,改進後的類的程式碼如下。 
  12.  * */
  13. publicclass PrintThread extends Thread {  
  14.     private RequestQueue queue;  
  15.     public PrintThread(String name, RequestQueue queue) {  
  16.         super(name);  
  17.         this.queue = queue;  
  18.     }  
  19.     publicvoid run() {  
  20.         synchronized(this) {  
  21.             //當前遍歷次數小於要求遍歷次數
  22.             while(queue.getiLoopNum() < RequestQueue.iLoopMaxNum ) {   
  23.                 queue.printMsg(this.getName());  
  24.             }             
  25.         }  
  26.     }  
  27. }  
  28. //RequestQueue 的程式碼
  29. package com.test.thread;  
  30. import java.util.LinkedList;  
  31. publicclass RequestQueue {  
  32.     public RequestQueue(String[] array) {  
  33.         for(String element: array) {  
  34.             queue.addLast(element);  
  35.         }  
  36.     }  
  37.     /**存放請求的連結串列*/
  38.     privatefinal LinkedList<String> queue = new LinkedList<String>();  
  39.     privateint iCurIndex = 0//當前資料在連結串列中的位置
  40.     privateint iLoopNum = 0;  //迴圈的次數
  41.     publicfinalstaticint iLoopMaxNum = 10//最大迴圈次數
  42.     /**把請求放入連結串列中*/
  43.     publicsynchronizedvoid printMsg(String name) {  
  44.         //根據當前讀取到的連結串列中的值來和執行緒的名字比較
  45.         while(!queue.get(iCurIndex).equals(name) && iLoopNum < 10) {   
  46.             try {  
  47.                 wait();  
  48.             } catch (InterruptedException e) {  
  49.             }  
  50.         }  
  51.         if(iLoopNum < iLoopMaxNum) {  
  52.             System.out.print(queue.get(iCurIndex));   
  53.             iCurIndex = (iCurIndex + 1) % queue.size();   
  54.             if(iCurIndex == 0) {  
  55.                 iLoopNum ++;  
  56.                 System.out.print(" ");//為了輸出好看,可以去掉
  57.             }  
  58.             notifyAll();              
  59.         }  
  60.     }  
  61.     publicint getiLoopNum() {  
  62.         return iLoopNum;  
  63.     }  
  64. }  
  65. //TestABC 的程式碼
  66. package com.test.thread;  
  67. publicclass TestABC {  
  68.     publicstaticvoid main(String[] args) {  
  69.         String[] array = new String[]{"A""B""C"};  
  70.         /**引數說明:array為要列印的字串陣列*/
  71.         RequestQueue queue = new RequestQueue(array);   
  72.         PrintThread threadA = new PrintThread("A", queue);  
  73.         PrintThread threadB = new PrintThread("B", queue);  
  74.         PrintThread threadC = new PrintThread("C", queue);  
  75.         threadA.start();  
  76.         threadB.start();  
  77.         threadC.start();  
  78.     }  
  79. }