CountDownLatch和CyclicBarrier模拟同时并发请求

 有时候要测试一下某个功能的并发能力,又不要想借助于其他测试工具,索性就自己写简单的demo模拟一个并发请求就最方便了。如果熟悉jemter的测试某接口的并发能力其实更专业,此处只是自己折腾着玩。

CountDownLatch和CyclicBarrier是jdk concurrent包下非常有用的两个并发工具类,它们提供了一种控制并发流程的手段。其实查看源码它们都是在内部维护了一个计数器控制流程的

CountDownLatch:一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;

CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再继续一起执行。

CountDownLatch和CyclicBarrier的区别

CountDownLatch的计数器,线程完成一个记录一个,计数器是递减  计数器,只能使用一次

CyclicBarrier的计数器 更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行,计数器是递增  计数器提供reset功能,可以多次使用

   另外Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。


  通常我们模拟并发请求,一般都是多开几个线程,发起请求就好了。但是方式,一般会存在启动的先后顺序了,算不得真正的同时并发!怎么样才能做到真正的同时并发呢?是本文想说的点,java中提供了闭锁 CountDownLatch, CyclicBarrier 刚好就用来做这种事就最合适了。

  下面分别使用CountDownLatch和CyclicBarrier来模拟并发的请求

CountDownLatch模拟

package com.test;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.util.concurrent.CountDownLatch;publicclass LatchTest {

    publicstaticvoidmain(String[] args)throws InterruptedException {

        Runnable taskTemp =new Runnable() {

       // 注意,此处是非线程安全的,留坑privateint iCounter;

            @Override

            publicvoid run() {

                for(inti = 0; i < 10; i++) {

                    // 发起请求//                    HttpClientOp.doGet("https://www.baidu.com/");iCounter++;

                    System.out.println(System.nanoTime() + " [" + Thread.currentThread().getName() + "] iCounter = " + iCounter);

                    try {

                        Thread.sleep(100);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

            }

        };

        LatchTest latchTest =new LatchTest();

        latchTest.startTaskAllInOnce(5, taskTemp);

    }

    publiclongstartTaskAllInOnce(intthreadNums,finalRunnable task)throws InterruptedException {

        finalCountDownLatch startGate =newCountDownLatch(1);

        finalCountDownLatch endGate =new CountDownLatch(threadNums);

        for(inti = 0; i < threadNums; i++) {

            Thread t =new Thread() {

                publicvoid run() {

                    try {

                        // 使线程在此等待,当开始门打开时,一起涌入门中                        startGate.await();

                        try {

                            task.run();

                        } finally {

                            // 将结束门减1,减到0时,就可以开启结束门了                            endGate.countDown();

                        }

                    } catch (InterruptedException ie) {

                        ie.printStackTrace();

                    }

                }

            };

            t.start();

        }

        longstartTime = System.nanoTime();

        System.out.println(startTime + " [" + Thread.currentThread() + "] All thread is ready, concurrent going...");

        // 因开启门只需一个开关,所以立马就开启开始门        startGate.countDown();

        // 等等结束门开启        endGate.await();

        longendTime = System.nanoTime();

        System.out.println(endTime + " [" + Thread.currentThread() + "] All thread is completed.");

        returnendTime - startTime;

    }

}

执行结果


CyclicBarrier模拟

// 与 闭锁 结构一致publicclass LatchTest {

    publicstaticvoidmain(String[] args)throws InterruptedException {


        Runnable taskTemp =new Runnable() {

            privateint iCounter;

            @Override

            publicvoid run() {

                // 发起请求//              HttpClientOp.doGet("https://www.baidu.com/");iCounter++;

                System.out.println(System.nanoTime() + " [" + Thread.currentThread().getName() + "] iCounter = " + iCounter);

            }

        };

        LatchTest latchTest =new LatchTest();//        latchTest.startTaskAllInOnce(5, taskTemp);latchTest.startNThreadsByBarrier(5, taskTemp);

    }

    publicvoidstartNThreadsByBarrier(intthreadNums, Runnable finishTask)throws InterruptedException {

        // 设置栅栏解除时的动作,比如初始化某些值CyclicBarrier barrier =new CyclicBarrier(threadNums, finishTask);

        // 启动 n 个线程,与栅栏阀值一致,即当线程准备数达到要求时,栅栏刚好开启,从而达到统一控制效果for(inti = 0; i < threadNums; i++) {

            Thread.sleep(100);

            newThread(new CounterTask(barrier)).start();

        }

        System.out.println(Thread.currentThread().getName() + " out over...");

    }

}classCounterTaskimplements Runnable {

    // 传入栅栏,一般考虑更优雅方式private CyclicBarrier barrier;

    publicCounterTask(final CyclicBarrier barrier) {

        this.barrier = barrier;

    }

    publicvoid run() {

        System.out.println(Thread.currentThread().getName() + " - " + System.currentTimeMillis() + " is ready...");

        try {

            // 设置栅栏,使在此等待,到达位置的线程达到要求即可开启大门            barrier.await();

        } catch (InterruptedException e) {

            e.printStackTrace();

        } catch (BrokenBarrierException e) {

            e.printStackTrace();

        }

        System.out.println(Thread.currentThread().getName() + " - " + System.currentTimeMillis() + " started...");

    }

}

执行结果


并发请求操作流程示意图如下:


  此处设置了一道门,以保证所有线程可以同时生效。但是,此处的同时启动,也只是语言层面的东西,也并非绝对的同时并发。具体的调用还要依赖于CPU个数,线程数及操作系统的线程调度功能等,不过咱们也无需纠结于这些了,重点在于理解原理!

  毕竟测试并发 还得用专业的工具 jmeter 还是很方便的.                                                                                         欢迎工作一到五年的Java工程师朋友们加入Java群: 891219277

群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容