java采用CountDownLatch +“多线程”----实现多个线程分别处理不同任务

本文介绍了一种利用多线程和CountDownLatch优化大量远程ESB接口调用速度的方法,通过并行处理,将接口响应时间从十几秒减少至三四秒,显著提升了系统性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在工作时,有一位同事遇到了一个查询优化的问题,过来向我请教。

       问题大致是这样的:要给前端提供一个查询的接口,而在此接口中要去调用远端的10几个ESB接口,由于要调用的远端ESB接口数量过多导致此接口响应异常缓慢,甚至于达到了十几秒钟。现在要想办法去优化这个接口。这十几个远端的ESB接口不用关心调用顺序,不需要互相依赖。

       针对于这个问题,我其实第一反应想到的是采用线程池,实现多线程调用,但同事告诉我说组长要求,先不要考虑使用线程池,因为线程池涉及到线程回收、维护等各种情况可能会在长时间运行后出现不可控的结果。

       第二反应我想到的是采用非阻塞的方式掉用这10几个远端的ESB接口,但是了解后发现非阻塞的在java中主要支持的是NIO和socket通讯,所以这种方式也不行。

       最后我想到的是,既然首选不能是线程池,那么能不能简单粗暴的采用多线程的模式?这样线程执行完毕后会自我销毁,就不用考虑回收维护等这些负责的情况了。开启多个线程让不同的线程去调用不同的远端ESB接口,在所有的线程都执行完毕之后再进行最终查询结果的整合。答案是可行的,直接采用Java自带的CountDownLatch + 多线程 去实现。具体实现过程大致如下(只是一个模拟过程,公司代码有规定不能外泄):

public class ThreadTest {

    //模拟不同的线程 返回不同的处理结果
    private Map map1 = new HashMap();
    private Map map2 = new HashMap();
    private Map map3 = new HashMap();

    /**
     * 主线程
     */
    @Test
    public void test() {

        //开启线程个数
        int threadCount = 3;
        //所有线程阻塞,然后统一开始
        CountDownLatch begin = new CountDownLatch(1);
        //主线程阻塞,直到所有分线程执行完毕
        CountDownLatch end = new CountDownLatch(threadCount);
        //开始多线程
        begin.countDown();
        for (Integer i = 0; i < threadCount; i++) {
            if (i == 0){
                Runnable runnable = dealSomeThing1(i, end,map1);
                new Thread(runnable).start();
            }
            if (i == 1){
                Runnable runnable = dealSomeThing2(i, end,map2);
                new Thread(runnable).start();
            }
            if (i == 2){
                Runnable runnable = dealSomeThing3(i, end,map3);
                new Thread(runnable).start();
            }
        }

        //多个线程都执行结束
        try {
            end.await();  //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
            System.out.println("多个线程都执行结束,可以做自己的事情了");
            String result1 = (String) map1.get("test");
            System.out.println(result1);
            String result2 = (String) map2.get("test");
            System.out.println(result2);
            String result3 = (String) map3.get("test");
            System.out.println(result3);
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("多线程执行中出错了,凉凉了!!!");
        }
    }


    /**
     * 工作线程
     * 本方法  是在构造多线程要做的事情
     * <p>
     * =====================可以做的事===================
     * 当然可以传入ConcurrentHashMap之类的线程安全的 类
     * 来记录线程中的处理结果之类的
     * 最后 在多线程都执行完了以后 就可以对处理结果进行操作了
     * ==================================================
     *
     * @param threadNum 当前线程编号
     * @param end
     * @return
     */
    private Runnable dealSomeThing1(int threadNum, CountDownLatch end,Map map) {
        Runnable runnable = new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println("线程" + threadNum + ":--------------------->开始工作");
                Thread.sleep(3000);//模拟调用目标ESB接口
                map.put("test","testResult1");
                end.countDown();  //执行完成后将count值减1
            }
        };
        return runnable;
    }

    private Runnable dealSomeThing2(int threadNum, CountDownLatch end,Map map2) {
        Runnable runnable = new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println("线程" + threadNum + ":--------------------->开始工作");
                Thread.sleep(3000);//模拟调用目标ESB接口
                map2.put("test","testResult2");
                end.countDown();  //执行完成后将count值减1
            }
        };
        return runnable;
    }

    private Runnable dealSomeThing3(int threadNum, CountDownLatch end,Map map3) {
        Runnable runnable = new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println("线程" + threadNum + ":--------------------->开始工作");
                Thread.sleep(3000);//模拟调用目标ESB接口
                map3.put("test","testResult3");
                end.countDown();  //执行完成后将count值减1
            }
        };
        return runnable;
    }
}

对CountDownLatch类的简单介绍如下:

    1、CountDownLatch是在java1.5被引入,跟它一起被引入的工具类还有CyclicBarrier、Semaphore、concurrentHashMap和BlockingQueue。

    2、存在于java.util.concurrent并发包下(所以线程也是安全的)。

    3、CountDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。

    4、CountDownLatch是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。

    5、CountDownLatch类有三个主要的方法:

       //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行

       public void await() throws InterruptedException { };  

       //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行

       public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; 

       //将count值减1

       public void countDown() { }; 

对于以上模拟的代码,开启了三个线程分别去调用了三个方法dealSomeThing1、dealSomeThing2、dealSomeThing3, 在每个方法执行完毕之后采用end.countDown()方法使count值减1,end.await()方控制当count值为0时(同时也表示所有的子线程已经执行完毕)开始对每个多线程的执行结果进行整合。按照往常的方式调用耗时至少是9秒钟。而按照这种方式耗时大约只有3到4秒钟,时间节约了至少一半。成功解决了此问题。

运行结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值