串行处理的优化方式有哪些?

前言

在做业务功能的时候,难免会遇到一个功能需要几部分的信息,结果获取完毕后,然后把它们聚合起来构造对应的响应信息,通常情况下我们会把这些毫无相关的业务处理串行的处理掉,这种处理方式当然可以解决这个业务。但是考虑到接口响应时间相关的问题时,或许我们可以使用其它办法来提高它的响应速度。

问题:串行处理

串行,顾名思义就是从上到下依次处理,各处理时间累加,从而导致了响应过慢等问题的出现。

public class SerialDemo {

    @Data
    static class Result {

        private String value1;

        private String value2;
    }

    public static void main(String[] args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        Result result = new Result();
        try {
            Thread.sleep(1000);
            result.setValue1("get t1 result");
        } catch (InterruptedException ignored) {
        }

        try {
            Thread.sleep(1000);
            result.setValue2("get t2 result");
        } catch (InterruptedException ignored) {
        }

        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());
        System.out.println(result);
    }
}
2018
SerialDemo.Result(value1=get t1 result, value2=get t2 result)

毫无意外的,处理时间大于2000ms+,接下来我们尝试使用join()解决该问题。

1.使用join()解决该问题

主线程创建并启动子线程,如果子线程需要进行大量的耗时运算,主线程往往将早于子线程结束之前结束,这时,如果主线程想要等待子线程执行完成之后再结束,就要用到join()了。join()的作用是等待线程对象销毁,关于join()想要进一步了解其原理的可以看看另一篇文章的对应小节

public class JoinDemo {

    @Data
    static class Result {

        private String value1;

        private String value2;
    }

    public static void main(String[] args) throws InterruptedException {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        Result result = new Result();
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(1000);
                result.setValue1("get t1 result");
            } catch (InterruptedException ignored) {
            }
        });

        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(1000);
                result.setValue2("get t2 result");
            } catch (InterruptedException ignored) {
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());
        System.out.println(result);
    }
}
1028
JoinDemo.Result(value1=get t1 result, value2=get t2 result)

处理耗时1028ms,相比较串行的处理方式,耗时大量减少!

join()当前线程等待join()的线程执行结束。原理是循环检查join()的线程是否存活,如果join()的线程存活则一直等待,当线程执行完毕后,调用this.notifyAll()进入后续流程。

// java.lang.Thread#join(long)
// millis默认为0
 if (millis == 0) {
     while (isAlive()) {
         wait(0);
     }
 }

除了这种方式还有没有其它办法呢 ?

2.使用CountDownLatch解决该问题

CountDownLatch允许一个或多个线程等待其它线程完成操作。

public class CountDownLatchDemo {

    @Data
    static class Result {

        private String value1;

        private String value2;
    }

    public static void main(String[] args) throws InterruptedException {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        CountDownLatch countDownLatch = new CountDownLatch(2);
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        Result result = new Result();
        threadPool.submit(() -> {
            try {
                Thread.sleep(1000);
                result.setValue1("get t1 result");
            } catch (InterruptedException ignored) {
            } finally {
                countDownLatch.countDown();
            }
        });

        threadPool.submit(() -> {
            try {
                Thread.sleep(1000);
                result.setValue2("get t2 result");
            } catch (InterruptedException ignored) {
            } finally {
                countDownLatch.countDown();
            }
        });

        countDownLatch.await();
        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());
        System.out.println(result);
    }
}
1041
CountDownLatchDemo.Result(value1=get t1 result, value2=get t2 result)

同样的,相比较串行的处理方式,耗时大量减少!

CountDownLatch使用给定的计数(N)进行初始化,当我们调用CountDownLatchcountDown()时,N = N - 1,CountDownLatchawait()会阻塞当前线程,直到N变成零。

3.使用CompletableFuture解决该问题

看名字我们就可以了解到,它是Future的增强与扩展。可以显式完成的Future,支持在完成时触发的相关功能和操作。

public class CompletableFutureDemo {

    @Data
    static class Result {

        private String value1;

        private String value2;
    }

    public static void main(String[] args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        Result result = new Result();
        @SuppressWarnings("unchecked")
        CompletableFuture<Void>[] completableFutures = new CompletableFuture[]{CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(1000);
                result.setValue1("get t1 result");
            } catch (InterruptedException ignored) {
            }
        }, threadPool), CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(1000);
                result.setValue2("get t2 result");
            } catch (InterruptedException ignored) {
            }
        }, threadPool)};
        CompletableFuture.allOf(completableFutures).join();
        System.out.println(result);
        stopWatch.stop();
        System.out.println(stopWatch.getTotalTimeMillis());
        threadPool.shutdown();
    }
}
1036
CompletableFutureDemo.Result(value1=get t1 result, value2=get t2 result)

CompletableFuture涉及到的内容比较多,后续会单独写一篇关于它API的介绍与使用的博客。

总结

类似的处理方式还有很多,本文就不一一列举了,本文简单介绍了使用join()CountDownLatchCompletableFuture的简单使用,本文介绍的比较粗浅,只是提供了一些优化思路,后续会针对CountDownLatchCompletableFuture单独写一篇详细的博客,好了,到此为止。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人生逆旅我亦行人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值