并发压测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();
    }
}


不知道,你有没有理解,在此也只是抛砖引玉一下,大家在实践中可以继续学习研究。

更多其他测试内容可以参考链接文末更多测试好文章


猜你喜欢

转载自blog.51cto.com/14010723/2299103