JAVA 模擬瞬間高並發


如何模擬一個並發?當時我的回答雖然也可以算是正確的,但自己感覺缺乏實際可以操作的細節,只有一個大概的描述。

      當時我的回答是:“線程全部在同一節點wait,然后在某個節點notifyAll。”

      面試官:“那你聽說過驚群效應嗎?”

      我:“我沒有聽過這個名詞,但我知道瞬間喚醒所有的線程,會讓CPU負載瞬間加大。”

      面試官:“那你有什么改進的方式嗎?”

      我:“采用阻塞技術,在某個節點將所有的線程阻塞,在利用條件,線程的個數達到一定數量的時候,打開阻塞。”

      面試官好像是比較滿意,結束了這個話題。

      面試結束后,我回頭這個塊進行了思考,要如何進行阻塞呢?我首先有一個思路就是,利用AtoInteger計算線程數,再利用synchronize方法塊阻塞一個線程,根據AtoInteger的判斷,執行sleep。

      代碼如下:

 

 
  1. /** 
  2.  * Created with IntelliJ IDEA. 
  3.  * User: 菜鳥大明 
  4.  * Date: 14-10-21 
  5.  * Time: 下午4:34 
  6.  * To change this template use File | Settings | File Templates. 
  7.  */  
  8. public class CountDownLatchTest1 implements Runnable{  
  9.     final AtomicInteger number = new AtomicInteger();  
  10.     volatile boolean bol = false;  
  11.   
  12.     @Override  
  13.     public void run() {  
  14.         System.out.println(number.getAndIncrement());  
  15.         synchronized (this) {  
  16.             try {  
  17.                 if (!bol) {  
  18.                     System.out.println(bol);  
  19.                     bol = true;  
  20.                     Thread.sleep(10000);  
  21.                 }  
  22.             } catch (InterruptedException e) {  
  23.                 e.printStackTrace();  
  24.             }  
  25.             System.out.println("並發數量為" + number.intValue());  
  26.         }  
  27.   
  28.     }  
  29.   
  30.     public static void main(String[] args) {  
  31.         ExecutorService pool = Executors. newCachedThreadPool();  
  32.         CountDownLatchTest1 test = new CountDownLatchTest1();  
  33.         for (int i=0;i<10;i++) {  
  34.             pool.execute(test);  
  35.         }  
  36.     }  
  37. }  

結果為:

 

 

 
  1. 0  
  2. 2  
  3. 1  
  4. 4  
  5. 3  
  6. false  
  7. 5  
  8. 6  
  9. 7  
  10. 8  
  11. 9  
  12. 並發數量為10  
  13. 並發數量為10  
  14. 並發數量為10  
  15. 並發數量為10  
  16. 並發數量為10  
  17. 並發數量為10  
  18. 並發數量為10  
  19. 並發數量為10  
  20. 並發數量為10  
  21. 並發數量為10  

 

從結果上來看,應該是可以解決問題,利用了同步鎖,volatile解決了同時釋放的問題,難點就在於開關。

后來查找資料,找到了一個CountDownLatch的類,專門干這個的

CountDownLatch是一個同步輔助類,猶如倒計時計數器,創建對象時通過構造方法設置初始值,調用CountDownLatch對象的await()方法則處於等待狀態,調用countDown()方法就將計數器減1,當計數到達0時,則所有等待者或單個等待者開始執行。

 

構造方法參數指定了計數的次數

 new CountDownLatch(1)  

 

countDown方法,當前線程調用此方法,則計數減一

 


 

cdAnswer.countDown();  

 

awaint方法,調用此方法會一直阻塞當前線程,直到計時器的值為0


 

cdOrder.await();  

直接貼代碼,轉載的代碼


 

 
  1. /** 
  2.  * 
  3.  * @author Administrator 
  4.  *該程序用來模擬發送命令與執行命令,主線程代表指揮官,新建3個線程代表戰士,戰士一直等待着指揮官下達命令, 
  5.  *若指揮官沒有下達命令,則戰士們都必須等待。一旦命令下達,戰士們都去執行自己的任務,指揮官處於等待狀態,戰士們任務執行完畢則報告給 
  6.  *指揮官,指揮官則結束等待。 
  7.  */  
  8. public class CountdownLatchTest {  
  9.   
  10.     public static void main(String[] args) {  
  11.         ExecutorService service = Executors.newCachedThreadPool(); //創建一個線程池  
  12.         final CountDownLatch cdOrder = new CountDownLatch(1);//指揮官的命令,設置為1,指揮官一下達命令,則cutDown,變為0,戰士們執行任務  
  13.         final CountDownLatch cdAnswer = new CountDownLatch(3);//因為有三個戰士,所以初始值為3,每一個戰士執行任務完畢則cutDown一次,當三個都執行完畢,變為0,則指揮官停止等待。  
  14.         for(int i=0;i<3;i++){  
  15.             Runnable runnable = new Runnable(){  
  16.                 public void run(){  
  17.                     try {  
  18.                         System.out.println("線程" + Thread.currentThread().getName() +  
  19.                                 "正准備接受命令");  
  20.                         cdOrder.await(); //戰士們都處於等待命令狀態  
  21.                         System.out.println("線程" + Thread.currentThread().getName() +  
  22.                                 "已接受命令");  
  23.                         Thread.sleep((long)(Math.random()*10000));  
  24.                         System.out.println("線程" + Thread.currentThread().getName() +  
  25.                                 "回應命令處理結果");  
  26.   
  27.                     } catch (Exception e) {  
  28.                         e.printStackTrace();  
  29.                     } finally {  
  30.                         cdAnswer.countDown(); //任務執行完畢,返回給指揮官,cdAnswer減1。  
  31.                     }  
  32.                 }  
  33.             };  
  34.             service.execute(runnable);//為線程池添加任務  
  35.         }  
  36.         try {  
  37.             Thread.sleep((long)(Math.random()*10000));  
  38.   
  39.             System.out.println("線程" + Thread.currentThread().getName() +  
  40.                     "即將發布命令");  
  41.             cdOrder.countDown(); //發送命令,cdOrder減1,處於等待的戰士們停止等待轉去執行任務。  
  42.             System.out.println("線程" + Thread.currentThread().getName() +  
  43.                     "已發送命令,正在等待結果");  
  44.             cdAnswer.await(); //命令發送后指揮官處於等待狀態,一旦cdAnswer為0時停止等待繼續往下執行  
  45.             System.out.println("線程" + Thread.currentThread().getName() +  
  46.                     "已收到所有響應結果");  
  47.         } catch (Exception e) {  
  48.             e.printStackTrace();  
  49.         } finally {  
  50.   
  51.         }  
  52.         service.shutdown(); //任務結束,停止線程池的所有線程  
  53.     }  
  54. }  

執行結果:

 

  1. 線程pool-1-thread-2正准備接受命令  
  2. 線程pool-1-thread-3正准備接受命令  
  3. 線程pool-1-thread-1正准備接受命令  
  4. 線程main即將發布命令  
  5. 線程pool-1-thread-2已接受命令  
  6. 線程pool-1-thread-3已接受命令  
  7. 線程pool-1-thread-1已接受命令  
  8. 線程main已發送命令,正在等待結果  
  9. 線程pool-1-thread-2回應命令處理結果  
  10. 線程pool-1-thread-1回應命令處理結果  
  11. 線程pool-1-thread-3回應命令處理結果  
  12. 線程main已收到所有響應結果  

 

上述也是一種實現方式,用countDownLatch的await()方法,代替了synchronize 和 sleep的阻塞功能,通過countDown的方法來當做開關,和計算線程數量的一種方式。

區別的話,肯定是后者會好一些,因為第一種方式依靠sleep(xxx)來阻塞把握不好最短時間,太短了,可能來沒有達到固定線程數就會打開開關。

至於兩者性能上的區別,目前我還不得而知,有機會測試一下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM