Jdk8 ParallelStream(并行流),自定义线程池,性能测试,okhttps同步及异步请求,干货,真实开发技术选型。

JDK8新特性(十二)之并行的Stream流 1.并行Stream流介绍 一般使用的Stream流是串行的,就是在一个线程上执行,效率并不是很高,因此JDK8也推出了一个新的概念,即并行的Stream流(parallelStream)。parallelStream其实就是一个并行执行的流,它通过默认的ForkJoinPool,可以提高多线程任务的速度。获取并行Stream流有两种方式: import java.util.ArrayList; import java.util.List; import java.util.stream.Stream 阅读详情

Jdk8 ParallelStream(并行流),自定义线程池,性能测试,okhttps同步及异步请求,为了使用上这个东西,昨晚研究了一番。我认为他只有在某些特殊场景下适用,如下:

来自OkHttps官网的简介及依赖说明

OkHttps 是 2020 年开源的对 OkHttp3 轻量封装的框架,它独创的异步预处理器,特色的标签,灵活的上传下载进度监听与过程控制功能,在轻松解决很多原本另人头疼问题的同时,设计上也力求纯粹与优雅。

OkHttps 默认依赖 OkHttp3 的 3.x 的最新 3.14.9 版本,但是已经全面兼容 OkHttp3 的 4.x 版本。默认依赖之所以不是 4.x,主要考虑到:

  • OkHttp3 4.x 是用 Kotlin 重写,包体相对较大(约是 3.x 的 1.8 倍)
  • 在没有 Kotlin 依赖的纯 Java 项目中,使用 4.x 会存在一些问题。
<dependency>
   <groupId>com.ejlchina</groupId>
   <artifactId>okhttps</artifactId>
   <version>3.5.3</version>
</dependency>
package jdk8ParallelStreamTest;

import com.ejlchina.okhttps.HTTP;
import com.ejlchina.okhttps.HttpResult;
import okhttp3.ConnectionPool;
import okhttp3.Response;
import org.apache.commons.httpclient.HttpStatus;
import org.springframework.util.StopWatch;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


public class Jdk8ParallelStreamTest {

    private static final DateTimeFormatter format = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss").toFormatter();

    // 日期转换为字符串
    public static String stringOfDate(Date date) {
        if (date != null) {
            LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
            return localDateTime.format(format);
        } else {
            return null;
        }
    }

    static class UrlInfoEntity {

        private String url;
        private int state;
        private Date warnTime;
        private String warnMsg;

        public UrlInfoEntity(String url) {
            this.url = url;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public int getState() {
            return state;
        }

        public void setState(int state) {
            this.state = state;
        }

        public Date getWarnTime() {
            return warnTime;
        }

        public void setWarnTime(Date warnTime) {
            this.warnTime = warnTime;
        }

        public String getWarnMsg() {
            return warnMsg;
        }

        public void setWarnMsg(String warnMsg) {
            this.warnMsg = warnMsg;
        }

        @Override
        public String toString() {
            return "{url:'" + url + '\'' +
                    ", state:" + state +
                    ", warnTime:" + stringOfDate(warnTime) +
                    ", warnMsg:'" + warnMsg + '\'' +
                    "}";
        }
    }

    HTTP http = HTTP.builder()
            .config(b -> {
                // 精细化配置
                // 连接超时时间(默认10秒)
                b.connectTimeout(10, TimeUnit.SECONDS);
                // 写入超时时间(默认10秒)
                b.writeTimeout(10, TimeUnit.SECONDS);
                // 读取超时时间(默认10秒)
                b.readTimeout(10, TimeUnit.SECONDS);
                // 配置连接池 最小10个连接(不配置默认为 5)
                b.connectionPool(new ConnectionPool(21, 7, TimeUnit.MINUTES));
                // 拦截器
                b.addInterceptor(chain -> {
                    int retryTimes = 1;
                    while (true) {
                        Response response = null;
                        try {
                            response = chain.proceed(chain.request());
                            if (response.code() != 200 && retryTimes < 4) {
                                log.warn(chain.request().url() + " 失败重试第" + retryTimes + "次!");
                                // 注意,这里一定要 close 掉失败的 Response
                                response.close();
                                retryTimes++;
                                continue;
                            }
                            return response;
                        } catch (Exception e) {
                            if (retryTimes < 4) {
                                log.warn(chain.request().url() + " 失败重试第" + retryTimes + "次!");
                                if (response != null) {
                                    // 注意,这里一定要 close 掉失败的 Response
                                    response.close();
                                }
                                retryTimes++;
                            } else {
                                if (response != null) {
                                    // 注意,这里一定要 close 掉失败的 Response
                                    response.close();
                                }
                                throw e;
                            }
                        } finally {
                            if (response != null) {
                                response.close();
                            }
                        }
                    }
                });
            }).build();

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        // 待处理数据
        List<UrlInfoEntity> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            UrlInfoEntity urlMonitorEntity;
            if (i % 5 == 0) {
                urlMonitorEntity = new UrlInfoEntity("https://www.baidu.com999");
            } else if (i % 5 == 1) {
                urlMonitorEntity = new UrlInfoEntity("https://ok.zhxu.cn/v4/introduction.html");
            } else if (i % 5 == 2) {
                urlMonitorEntity = new UrlInfoEntity("https://developer.mozilla.org/zh-CN/docs/Web/CSS");
            } else if (i % 5 == 3) {
                urlMonitorEntity = new UrlInfoEntity("https://www.runoob.com/java/java-tutorial666.html");
            } else {
                urlMonitorEntity = new UrlInfoEntity("https://mvnrepository.com/");
            }
            list.add(urlMonitorEntity);
        }

        StopWatch stopWatch = new StopWatch("计秒表");

        /* stopWatch.start("使用单线程耗时:");
        test1(list);
        stopWatch.stop();
        System.out.println(stopWatch.getLastTaskName() + stopWatch.getLastTaskTimeMillis() + "ms"); */

        /* stopWatch.start("使用默认线程池耗时:");
        test2(list);
        stopWatch.stop();
        System.out.println(stopWatch.getLastTaskName() + stopWatch.getLastTaskTimeMillis() + "ms"); */

        /* stopWatch.start("使用ForkJoinPool自定义线程池耗时:");
        test3(list);
        stopWatch.stop();
        System.out.println(stopWatch.getLastTaskName() + stopWatch.getLastTaskTimeMillis() + "ms"); */

        /* stopWatch.start("使用ForkJoinPool自定义线程池及同步请求耗时:");
        test5(list);
        stopWatch.stop();
        System.out.println(stopWatch.getLastTaskName() + stopWatch.getLastTaskTimeMillis() + "ms"); */

        stopWatch.start("使用ForkJoinPool自定义线程池及异步请求耗时:");
        test6(list);
        stopWatch.stop();
        System.out.println(stopWatch.getLastTaskName() + stopWatch.getLastTaskTimeMillis() + "ms");

        /* stopWatch.start("使用单线程异步请求耗时:");
        test7(list);
        stopWatch.stop();
        System.out.println(stopWatch.getLastTaskName() + stopWatch.getLastTaskTimeMillis() + "ms"); */
    }

    // 使用单线程
    private static void test1(List<UrlInfoEntity> list) {
        list.forEach(urlInfo -> {
            try {
                // 模拟延时,省时不设置那么大
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println("list1 size:" + list.size() + "\nlist1:" + list);
    }

    // 使用默认线程池
    private static void test2(List<UrlInfoEntity> list) {
        list.parallelStream().forEach(urlInfo -> {
            try {
                // 模拟延时,省时不设置那么大
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println("list2 size:" + list.size() + "\nlist2:" + list);
    }

    // 使用ForkJoinPool自定义线程池
    private static void test3(List<UrlInfoEntity> list) throws ExecutionException, InterruptedException {
        ForkJoinPool customThreadPool = new ForkJoinPool(35);
        List<UrlInfoEntity> list3 = customThreadPool.submit(() -> list.parallelStream().peek(urlInfo -> {
            try {
                // 模拟延时,省时不设置那么大
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).collect(Collectors.toList())).get();
        System.out.println("list3 size:" + list3.size() + "\nlist3:" + list3);
    }

    // 使用ForkJoinPool自定义线程池,同步请求
    private static void test5(List<UrlInfoEntity> list) throws ExecutionException, InterruptedException {
        ForkJoinPool customThreadPool = new ForkJoinPool(35);
        List<UrlInfoEntity> list5 = customThreadPool.submit(() -> list.parallelStream().peek(urlInfo -> {
            // 同步,方法 nothrow() 让异常不直接抛出
            String url = urlInfo.getUrl();
            HttpResult result = http.sync(urlInfo.getUrl()).nothrow().get();
            monitorAddress(urlInfo, result, url);
        }).collect(Collectors.toList())).get();
        System.out.println("list5 size:" + list5.size() + "\nlist5:" + list5);
    }

    // 使用ForkJoinPool自定义线程池,异步请求
    private static void test6(List<UrlInfoEntity> list) throws ExecutionException, InterruptedException {
        ForkJoinPool customThreadPool = new ForkJoinPool(35);
        List<UrlInfoEntity> list6 = customThreadPool.submit(() -> list.parallelStream().peek(urlInfo -> {
            // 异步,方法 nothrow() 让异常不直接抛出
            String url = urlInfo.getUrl();
            HttpResult result = http.async(url).nothrow().get().getResult();
            monitorAddress(urlInfo, result, url);
        }).collect(Collectors.toList())).get();
        System.out.println("list6 size:" + list6.size() + "\nlist6:" + list6);
    }

    public static void monitorAddress(UrlInfoEntity urlInfo, HttpResult result, String url) {
        if (result.getStatus() == HttpStatus.SC_OK) {
            // 相当于continue终止的是这一轮的list遍历
            return;
        } else if (!"?wsdl".equals(url.substring(url.length() - 5)) && result.getStatus() == HttpStatus.SC_INTERNAL_SERVER_ERROR &&
                result.getBody() != null && result.getBody().toString().contains("HTTP GET PATH_INFO")) {
            // 对wsdl地址做兼容
            return;
        }
        urlInfo.setWarnTime(Date.from(Instant.now()));
        urlInfo.setState(result.getStatus());
        // 判断执行状态
        switch (result.getState()) {
            case RESPONSED:     // 请求已正常响应
                try {
                    // 模拟延时,省时不设置那么大
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                urlInfo.setWarnMsg("请求已响应,但返回:" + HttpStatus.getStatusText(result.getStatus()));
                break;
            case CANCELED:      // 请求已被取消
                urlInfo.setWarnMsg("请求已被取消");
                break;
            case NETWORK_ERROR: // 网络错误,说明用户没网了
                urlInfo.setWarnMsg("网络错误");
                break;
            case TIMEOUT:       // 请求超时
                urlInfo.setWarnMsg("请求超时");
                break;
            case EXCEPTION:     // 其它异常
                urlInfo.setWarnMsg("其它异常");
                break;
        }
        // 还可以获得具体的异常信息
        System.out.println(Thread.currentThread().getName() + " --> " + urlInfo + "<--异常:" + result.getError());
    }

    // 使用单线程异步请求耗时
    private static void test7(List<UrlInfoEntity> list) {
        list.forEach(urlInfo -> {
            // 异步,方法 nothrow() 让异常不直接抛出
            String url = urlInfo.getUrl();
            HttpResult result = http.async(urlInfo.getUrl()).nothrow().get().getResult();
            monitorAddress(urlInfo, result, url);
        });
        System.out.println("list7 size:" + list.size() + "\nlist7:" + list);
    }

}

test方法要一个个跑啊,加上初始化需要的时间等,都是为了准确性。

这两个单独对比使用了http调用地址且有重试机制,异步请求按理来说是快更多的,主要这里没模拟更复杂的业务场景。

只要有重试机制在单线程异步请求是绝对更慢的,就不跟这两个比了,当然代码提供了。

转载请注明出处,原创方案技术。

多线程之parallelStream parallelStream默认使用线程池ForkJoinPool来调度的,而ForkJoinPool的默认线程数是CPU核数 - 1。总结:在使用parallelStream时要注意根据需求选择合适的线程池,避免出现线程池溢出等一系列问题。输出:使用spring框架中的 ForkJoinPool.commonPool。输出:使用的依旧是 ForkJoinPool.commonPool。举例二:使用其他线程池,如 Executors.输出:使用自定的ForkJoinPool线程池 阅读详情

相关推荐

无人驾驶感知融合——横摆角速度的多源估计算法解析

本文深入解析了无人驾驶感知融合中的横摆角速度多源估计算法,详细介绍了轮速差法、侧向加速度法、曲率法和惯性传感器直读法四种计算方法及其优缺点。通过卡尔曼滤波实战案例,展示了多源融合算法在提升精度和稳定性方面的显著优势,并探讨了工程实践中的时间对齐、坐标系统一等关键问题。

weixin_31458015的博客 433

Java8并行流自定义线程池操作示例

主要介绍了Java8并行流自定义线程池操作,结合实例形式分析了并行流的相关概念、定义及自定义线程池的相关操作技巧,需要的朋友可以参考下

别再死磕AUTOSAR文档了!手把手教你用ISOLAR-A配置BswM模块(附实战流程图)

本文通过Vector ISOLAR-A工具实战演示AUTOSAR BswM模块的配置流程,帮助工程师快速掌握基础软件模式管理。从环境准备到模式仲裁规则配置,再到动作列表实现,详细讲解每个步骤,并提供常见问题排查和高级优化技巧,大幅提升开发效率。

weixin_42542669的博客 218

JAVA-- 在Java8 Parallel Stream中如何自定义线程池

使用Parallel Stream时,在适当的环境中,通过适当地使用并行度级别,可以在某些情况下获得性能提升。 如果程序创建一个自定义ThreadPool,必须记住调用它的shutdown()方法来避免内存泄漏。 本文通过实际代码示例,让你快速理解如何自定义线程池

penriver的博客 8786

聊聊如何自定义parallelStream线程池

本文主要研究一下parallelStream怎么使用自定义线程池

go4it 2342

parallelStream并发流的线程知识,以及自定义线程方法

parallelStream并发流的线程知识,以及自定义线程方法

weixin_59244784的博客 3399

Java8 并行流(parallelStream)原理分析及线程池线数设置

我们都知道在java 使用strem流做多线程处理是非常方便的。 list.parallelStream().forEach(s -> { // 业务处理 }); 但是parallelStream是如何实现多线程处理的呢?其实看源码我们会发现parallelStream是使用线程池ForkJoin来调度的,这里我们可以看源码看到 由于我们使用的 forEach,所以直接看类ForEachOps即可.当然其他方法比如reduce等会有不同的实现类,总的实现类是如

念念不忘,必有回响 2万+

ParallelStream流--线程数的控制

Java 8 并行流(parallel stream)采用共享线程池,对性能造成了严重影响。底层使用通用的 fork/join 池来实现,该池是所有并行流共享的。默认情况,fork/join 池会为每个处理器分配一个线程。 public static void main(String[] args) throws Exception { List<String> list = Arrays.asList("1", "2", "3", "4", "5"); //

qq_34748010的博客 8578

多线程之自定义线程池

1 自义定线程池1定义2 组成部分自定义线程池组成部分: 核心线程数,等待超时时间,等待时间单位,任务队列,拒绝策略.

ABestRookie的博客 859

通过线程池方式改造Stream.parallel()并行流

大家好,我是哪吒。上一篇简单聊一聊公平锁和非公平锁,parallel并行流,提到了一个IntStream.rangeClosed并行流问题,很多小伙伴,对这个比较陌生,想用线程池的方式改造一下。 二、线程池方式改造 不会那些新特性,还是原始的香啊,写起代码,得心应手。这时候,有些小伙伴,又陷入了选择恐惧症。用哪个线程池比较好呢?简单回顾一下:最常用的还是固定线程数线程池newFixedThreadPool(10)。 2、线程类 3、信心满满,走起来 我草,这不对啊,不应该是100000嘛?又把老子整不会了

学Java,找哪吒 2777

Java学习----线程池

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收重用时则新建线程。}工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger.MAX_VALUE), 这样可灵活的往线程池中添加线程。如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。

hanxuya的博客 1438

java8parallelStream并发

java8之stream 一、stream介绍 1、基础用法 二、parallelStream并行 parallelStream并行,底层实现采用ForkJoin,线程池大小默认为:cpu核心数 *获取cpu核心数代码: Runtime.getRuntime().availableProcessors(); *手动设置ForkJoin线程池大小 // 线程池大小配置 String FORK_JOIN_POOL_PARALLELISM = "java.util.concurrent.ForkJoinPool

ylf尘风的博客 1567

深入浅出parallelStream

about Stream什么是流? Stream是java8中新增加的一个特性,被java猿统称为流. Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 1

梦铃之境的专栏 10万+

Java并行流Parallel Stream与Fork-Join线程池

Fork-Join Parallel Stream实现任务的切分,并将任务提交到全局的ForkJoinPool线程池中执行,注意,是全局的线程池。关于ForkJoinPool,我这里简单介绍下。 在Fork-Join中,比如一个拥有4个线程的ForkJoinPool线程池,有一个任务队列,一个大的任务切分出的子任务会提交到线程池的任务队列中,4个线程从任务队列中获取任务执行,哪个线程执行的任务快,哪个线程执行的任务就多,只有队列中没有任务线程才是空闲的,这就是工作窃取。可以这样理解工作窃取,比如有4个人干

njitzyd 5342

java parallelStream接口配置线程池数量

【代码】java parallelStream接口配置线程池数量。

路过君的博客 3671

Java 多线程之并行流parallelStream

并行流Java中Stream API的一部分,用于在多核处理器上并行执行流操作。在Java 8及更高版本中引入的Stream API提供了一种新的处理集合的方式,使得开发者能够更轻松地进行数据处理和操作。在使用Stream API时,可以将集合转换为流,然后进行各种操作,例如过滤、映射、排序等。在这个过程中,流可以是串行流(Sequential Stream)或并行流(Parallel Stream)。并行流通过使用多线程并行处理数据,充分利用多核处理器的优势,从而在某些情况下提高处理速度。

QIFU的专栏 1万+

JDK 8-集合框架进阶】6.1 parallelStream 并行流

一、parallelStream并行流 1.1 串行 和 并行的区别 > 执行结果 二、问题 2.1 paralleStream 并行是否一定比 Stream 串行快? 2.2 是否可以都用并行? > 报错 三、实战 > 执行结果 四、总结

ladymorgana的博客 505

Java并行流陷阱:为什么指定线程池可能是个坏主意

任务为递归型任务,任务可以划分为子任务,空闲线程可以”窃取“待执行任务,充分利用线程池。最后,需要进行性能测试。但是,由于公共池是公用的,每一次任务的成功执行不一定能保证整体执行多条任务时能够执行成功(这也是推荐使用自定义线程池的原因之一)。理论上,流的处理过程中,所有的方法都应该是纯函数,遵循引用透明原则,内部可以对具体执行流程进行优化,其不为 IO 密集型任务是理所应当的。,认为没必要,parallel 提供了简单直接的使用方式,官方的初衷就是流式编程需要轻松实现并行支持,而指定线程池会使API更。

Maisucsdn的博客 286

Java 8 ParallelStream:高效处理数据的利器

Java 8parallelStream为并行计算提供了便捷方式,能够显著提升大数据量处理的性能。本文介绍了并行流的基本使用方法,并重点探讨了三大注意事项:线程安全问题(推荐使用线程安全集合或collect方法)、性能评估(小数据量可能适得其反)以及执行顺序问题(并行流顺序不确定)。此外还介绍了自定义线程池和分段合并操作等高级用法。正确使用并行流可以在多核环境下获得性能提升,但需根据任务复杂度选择是否使用并行处理。

hello! 6862

Java8新特性JDK8并行流parallelStream

Java新特性玩转JDK8并行流parallelStream 为什么会有这个并行流? 集合做重复的操作,如果使用串行执行会相当耗时,因此一般会采用多线程来加快, Java8的paralleStream用fork/join框架提供了并发执行能力 底层原理 线程池(ForkJoinPool)维护一个线程队列 可以分割任务,将父任务拆分成子任务,完全贴合分治思想 stream和parallelStream区别 public class Main { public static v

weixin_44368487的博客 3353
上一篇: python题目用java解决
下一篇: Win11 安装 AlmaLinux 9 到指定目录
熬夜的猪
博客等级 码龄9年 1353粉丝 · 40原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值