JAVA 模擬瞬間高並發


      前些日子接到了一個面試電話。面試內容我印象非常深,怎樣模擬一個並發?當時我的回答盡管也能夠算是正確的,但自己感覺缺乏實際能夠操作的細節,僅僅有一個大概的描寫敘述。

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

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

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

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

      我:“採用堵塞技術。在某個節點將全部的線程堵塞,在利用條件。線程的個數達到一定數量的時候。打開堵塞。

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

      面試結束后,我回頭這個塊進行了思考。要怎樣進行堵塞呢?我首先有一個思路就是。利用AtoInteger計算線程數,再利用synchronize方法塊堵塞一個線程,依據AtoInteger的推斷,運行sleep。

      代碼例如以下:

/**
 * Created with IntelliJ IDEA.
 * User: 菜鳥大明
 * Date: 14-10-21
 * Time: 下午4:34
 * To change this template use File | Settings | File Templates.
 */
public class CountDownLatchTest1 implements Runnable{
    final AtomicInteger number = new AtomicInteger();
    volatile boolean bol = false;

    @Override
    public void run() {
        System.out.println(number.getAndIncrement());
        synchronized (this) {
            try {
                if (!bol) {
                    System.out.println(bol);
                    bol = true;
                    Thread.sleep(10000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("並發數量為" + number.intValue());
        }

    }

    public static void main(String[] args) {
        ExecutorService pool = Executors. newCachedThreadPool();
        CountDownLatchTest1 test = new CountDownLatchTest1();
        for (int i=0;i<10;i++) {
            pool.execute(test);
        }
    }
}
結果為:

0
2
1
4
3
false
5
6
7
8
9
並發數量為10
並發數量為10
並發數量為10
並發數量為10
並發數量為10
並發數量為10
並發數量為10
並發數量為10
並發數量為10
並發數量為10

從結果上來看,應該是能夠解決這個問題,利用了同步鎖,volatile攻克了同一時候釋放的問題,難點就在於開關。

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

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


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

new CountDownLatch(1)

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

cdAnswer.countDown();

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

cdOrder.await();

直接貼代碼,轉載的代碼

/**
 *
 * @author Administrator
 *該程序用來模擬發送命令與運行命令,主線程代表指揮官。新建3個線程代表戰士,戰士一直等待着指揮官下達命令,
 *若指揮官沒有下達命令,則戰士們都必須等待。

一旦命令下達,戰士們都去運行自己的任務。指揮官處於等待狀態,戰士們任務運行完成則報告給  *指揮官。指揮官則結束等待。  */ public class CountdownLatchTest {     public static void main(String[] args) {         ExecutorService service = Executors.newCachedThreadPool(); //創建一個線程池         final CountDownLatch cdOrder = new CountDownLatch(1);//指揮官的命令。設置為1,指揮官一下達命令。則cutDown,變為0,戰士們運行任務         final CountDownLatch cdAnswer = new CountDownLatch(3);//由於有三個戰士,所以初始值為3,每個戰士運行任務完成則cutDown一次,當三個都運行完成,變為0。則指揮官停止等待。         for(int i=0;i<3;i++){             Runnable runnable = new Runnable(){                 public void run(){                     try {                         System.out.println("線程" + Thread.currentThread().getName() +                                 "正准備接受命令");                         cdOrder.await(); //戰士們都處於等待命令狀態                         System.out.println("線程" + Thread.currentThread().getName() +                                 "已接受命令");                         Thread.sleep((long)(Math.random()*10000));                         System.out.println("線程" + Thread.currentThread().getName() +                                 "回應命令處理結果");                     } catch (Exception e) {                         e.printStackTrace();                     } finally {                         cdAnswer.countDown(); //任務運行完成,返回給指揮官,cdAnswer減1。                     }                 }             };             service.execute(runnable);//為線程池加入任務         }         try {             Thread.sleep((long)(Math.random()*10000));             System.out.println("線程" + Thread.currentThread().getName() +                     "即將公布命令");             cdOrder.countDown(); //發送命令,cdOrder減1,處於等待的戰士們停止等待轉去運行任務。             System.out.println("線程" + Thread.currentThread().getName() +                     "已發送命令,正在等待結果");             cdAnswer.await(); //命令發送后指揮官處於等待狀態。一旦cdAnswer為0時停止等待繼續往下運行             System.out.println("線程" + Thread.currentThread().getName() +                     "已收到全部響應結果");         } catch (Exception e) {             e.printStackTrace();         } finally {         }         service.shutdown(); //任務結束。停止線程池的全部線程     } }

運行結果:

線程pool-1-thread-2正准備接受命令
線程pool-1-thread-3正准備接受命令
線程pool-1-thread-1正准備接受命令
線程main即將公布命令
線程pool-1-thread-2已接受命令
線程pool-1-thread-3已接受命令
線程pool-1-thread-1已接受命令
線程main已發送命令,正在等待結果
線程pool-1-thread-2回應命令處理結果
線程pool-1-thread-1回應命令處理結果
線程pool-1-thread-3回應命令處理結果
線程main已收到全部響應結果

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

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

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




免責聲明!

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



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