併發壓測java腳本你一定要會的3個類

性能測試做到後面,一些特殊的場景利用常用的現成工具滿足不了需求,所以你需要學習java寫一些特定協議的壓測腳本,那你不得不研究多線程或線程池,而此時你也一定會遇到java併發編程中的幾個類,今天重點講解這3個類,CountDownLanch、CyclicBarrier、Semaphore,希望大家以後看到的時候知道是幹嘛用的。


接下來,我就最近學習的成果,下面和大家舉例子講解一下,幫助理解。


1CountDownLanch


場景

工作中往往會遇到需要異步處理的任務,此時我們就會利用多線程的方式去處理,即啓動子線程去執行任務,而此時主線程阻塞,等待所有的子線程完成任務後,再去做一些彙總統計工作。


CountDownLanch 是一個倒數計數器, 給一個初始值(>=0), 然後每一次調用countDown就會減1, 這很符合等待多個子線程結束的場景: 一個線程結束的時候, countDown一次, 直到所有的線程都countDown了 , 那麼所有子線程就都結束了。

先看看CountDownLanch提供的方法。

await: 會阻塞等待計數器減少到0位置. 帶參數的await是多了等待時間。

countDown: 將當前的計數減1。

getCount(): 返回當前的計數。


顯而易見, 我們只需要在子線程執行之前, 賦予初始化countDownLanch, 並賦予線程數量爲初始值。

每個線程執行完畢的時候, 就countDown一下。主線程只需要調用await方法, 可以等待所有子線程執行結束。


例子

package ht.test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;

public class CountDownLatchTest {

    // 參數是2表示對象執行2次countDown方法才能釋放鎖
    static CountDownLatch c = new CountDownLatch(2);

    public static void main(String[] args) throws InterruptedException {
        // 定義線程數
        int subThreadNum = 5;
        // 取得一個倒計時器,從5開始
        CountDownLatch countDownLatch = new CountDownLatch(subThreadNum);
        // 依次創建5個線程,並啓動
        for (int i = 0; i < subThreadNum; i++) {
            SubThread runnablethread = new SubThread(5000, countDownLatch);
            new Thread(runnablethread).start();

        }
        // 主線程工作,此處可以添加一些額外的工作
        // mainWork();
        // 等待所有的子線程結束
        countDownLatch.await();
        System.out.println("all all all Main Thread work done!");

    }

    static class SubThread implements Runnable {

        private CountDownLatch countDownLatch;
        private long workTime;

        public SubThread(long workTime, CountDownLatch countDownLatch) {
            this.workTime = workTime;
            this.countDownLatch = countDownLatch;
        }

        public void run() {
            // TODO Auto-generated method stub
            try {
                System.out.println("線程Id:" + Thread.currentThread().getId() + " Sub thread is starting!");
                Thread.sleep(workTime);

                System.out.println("線程Id:" + Thread.currentThread().getId() + " Sub thread is stopping!");
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                // 線程結束時,將計時器減一
                countDownLatch.countDown();
            }
        }
    }

}



2CyclicBarrier


場景

我們在做壓測的時候,如要真正的併發,那麼在創建線程成功後需要等待其他線程也創建好了,一起等着,同時發送請求,此時就用到了CyclicBarrier。

CyclicBarrier的字面意思是可循環使用(Cyclic)的屏障(Barrier)。它要做的事情是,讓一組線程到達一個屏障(也可以叫同步點)時被阻塞,直到最後一個線程到達屏障時,屏障纔會開門,所有被屏障攔截的線程纔會繼續運行。


例子

package ht.test;

import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierTest {

    // 初始化線程等待的個數,當調用await()函數的次數和這裏的數值一致時候,接下去執行。
    static CyclicBarrier c = new CyclicBarrier(2);

    public static void main(String[] args) throws Exception {

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // 子線程
                    c.await();

                } catch (Exception e) {

                }
                System.out.println(1);
            }
        }).start();

        try {
            // 主線程,若此處註冊掉,執行結果爲2,並且子線程一直阻塞着
            c.await();
        } catch (Exception e) {

        }
        System.out.println(2);
    }
}



3Semaphore


場景

有時候併發太大的時候,我們需要人工的控制,譬如一些數據庫的連接數這樣子的,資源畢竟有限的,不可能無限去創建連接,此時我們就需要利用Semaphore去控制。

Semaphore(信號量)是用來控制同時訪問特定資源的線程數量,它通過協調各個線程,以保證合理的使用公共資源。可以控制系統的流量,拿到信號量的線程可以進入,否則就等待。通過acquire()和release()獲取和釋放訪問許可。


例子

package ht.test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class TestSemaphore {
    public static void main(String[] args) {
        // 線程池
        ExecutorService exec = Executors.newCachedThreadPool();
        // 只能5個線程同時訪問
        final Semaphore semp = new Semaphore(5);
        // 模擬20個客戶端訪問
        for (int index = 0; index < 20; index++) {
            final int NO = index;
            Runnable run = new Runnable() {
                @Override
                public void run() {
                    try {
                        // 獲取許可
                        semp.acquire();
                        System.out.println("Accessing: " + NO);
                        // 模擬實際業務邏輯
                        Thread.sleep((long10000);
                        // 訪問完後,釋放
                        semp.release();
                    } catch (InterruptedException e) {
                    }
                }
            };
            exec.execute(run);
        }

        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // System.out.println(semp.getQueueLength());
        // 退出線程池
        exec.shutdown();
    }
}


不知道,你有沒有理解,在此也只是拋磚引玉一下,大家在實踐中可以繼續學習研究。

更多其他測試內容可以參考鏈接文末更多測試好文章


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章