JAVA 模擬瞬間高併發

      前些日子接到了一個面試電話。面試內容我印象很是深,怎樣模擬一個併發?當時個人回答儘管也可以算是正確的,但本身感受缺少實際可以操做的細節,僅僅有一個大概的描寫敘述。java

      當時個人回答是:「線程全部在同一節點wait,而後在某個節點notifyAll。」面試

      面試官:「那你據說過驚羣效應嗎?」併發

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

ide

      面試官:「那你有什麼改進的方式嗎?」post

      我:「採用堵塞技術。在某個節點將所有的線程堵塞,在利用條件。線程的個數達到必定數量的時候。打開堵塞。性能

this

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

      面試結束後,我回頭這個塊進行了思考。要怎樣進行堵塞呢?我首先有一個思路就是。利用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)來堵塞把握很差最短期,過短了,可能來沒有達到固定線程數就會打開開關。

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

相關文章
相關標籤/搜索