【优雅代码】04-1行代码完成多线程,别再写runnable了

欢迎关注b站账号/公众号【六边形战士夏宁】,一个要把各项指标拉满的男人。该文章已在github目录收录。

屏幕前的大帅比大漂亮如果有帮助到你的话请顺手点个赞、加个收藏这对我真的很重要。别下次一定了,都不关注上哪下次一定。

1.背景介绍

java8提供的CompletableFuture以及匿名函数可以让我们一行代码完成多线程

2.建立相关类

2.1.ThreadEntity

用于多线程测试的实体类

public class ThreadEntity {
private int num;
private int price;
public int countPrice(){
price = RandomUtils.nextInt();
try {
System.out.println(num);
// 随机等待1~10秒
Thread.sleep(RandomUtils.nextInt(1, 10) * 1000);
System.out.println(num);
} catch (InterruptedException e) {
e.printStackTrace();
}
return price;
}
}

2.2.ThreadPoolManager

/**
* tasks 每秒的任务数,默认200,依据访问量及使用线程池的地方进行计算
* taskCost:每个任务花费时间,默认0.1s
* responseTime:最大响应时间,默认为1s,一般用户最大忍受时间为3秒
*
* @author seal email:876651109@qq.com
* @date 2020/5/30 10:08 AM
*/
@Data
@Slf4j
@Configuration
public class ThreadPoolManager {
/**
* 平均响应时间默认2秒
*/
private static final float ALL_COST_AVG = 2F;
/**
* 平均IO时间默认1.5秒
*/
private static final float IO_COST_AVG = 1.5F;
/**
* 服务器核数
*/
private static final int SIZE_PROCESSOR = Runtime.getRuntime().availableProcessors();
/**
* https://www.cnblogs.com/dennyzhangdd/p/6909771.html?utm_source=itdadao&utm_medium=referral
* 阻塞系数=阻塞时间/(阻塞时间+计算时间)
* 线程数=核心数/(1-阻塞系数)
* 等同于CPU核心数*cpu使用率*(1+等待时间与计算时间的比率)
* N+1通常为最优效率
* <p>
* https://blog.51cto.com/13527416/2056080
*/
private static final int SIZE_CORE_POOL = SIZE_PROCESSOR + 1; /**
* 线程池维护最大数量,默认会与核心线程数一致无意义,保守情况取2cpu
* 或者使用简单计算 线程池大小 = ((线程 IO time + 线程 CPU time )/线程 CPU time ) CPU数目**
* 请求所消耗的时间 /(请求所消耗的时间-DB处理)*CPU数目,重点在于cpu等待时间,通常为数据库DB时间
* 按照通常2秒展示界面,数据库运算1.5秒则(2/0.5)*n,其实就是优化等待时间
* <p>
* 默认4n即8核32线程
*/
private static final int SIZE_MAX_POOL = (int) (SIZE_PROCESSOR * (ALL_COST_AVG / (ALL_COST_AVG - IO_COST_AVG)));
/**
* 线程池队列长度,默认为integer最大值,Dubbo使用1000,无限大会引起用户用户的任务一直排队,应选择适当性丢弃,
* 可忍受时间6其它的则抛弃
* SIZE_MAX_POOL/IO_COST_AVG=每秒可处理任务数,默认为
* 可忍受时间6*每秒可处理任务数=X队列数
*/
private static final int SIZE_QUEUE = (int) (6 * (SIZE_MAX_POOL / IO_COST_AVG));
/**
* 线程池具体类
* LinkedBlockingDeque常用于固定线程,SynchronousQueue常用于cache线程池
* Executors.newCachedThreadPool()常用于短期任务
* <p>
* 线程工厂选择,区别不大
* 有spring的CustomizableThreadFactory,new CustomizableThreadFactory("springThread-pool-")
* guava的ThreadFactoryBuilder,new ThreadFactoryBuilder().setNameFormat("retryClient-pool-").build();
* apache-lang的BasicThreadFactory,new BasicThreadFactory.Builder().namingPattern("basicThreadFactory-").build()
* <p>
* 队列满了的策略默认AbortPolicy
*/
private static ThreadPoolManager threadPoolManager = new ThreadPoolManager(); private final ThreadPoolExecutor pool = new ThreadPoolExecutor(
SIZE_CORE_POOL,
SIZE_MAX_POOL,
30L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(SIZE_QUEUE),
new CustomizableThreadFactory("springThread-pool-"),
new ThreadPoolExecutor.AbortPolicy()
); private void prepare() {
if (pool.isShutdown() && !pool.prestartCoreThread()) {
int coreSize = pool.prestartAllCoreThreads();
System.out.println("当前线程池");
}
} public static ThreadPoolManager getInstance() {
if (threadPoolManager != null) {
ThreadPoolExecutor pool = threadPoolManager.pool;
}
return threadPoolManager;
}
}

3.核心代码

3.1.并行流

parallel是并行核心可以发现内部是多线程运行,但是经过collect以后会排好序所以不用担心,小项目可以使用,大项目的话建议老老实实用自己的线程池,JDK自带的fork/join并不贴合业务

System.out.println(Stream.of(1, 2, 3, 4, 5, 6).parallel().map(l -> {
System.out.println(l);
return l;
}).collect(Collectors.toList()));

输出如下,因为多线程所以随机输出,但因为使用collect收集则最终结果并未发生改变

2
6
4
5
3
1
[1, 2, 3, 4, 5, 6]

3.2.同步代码

这个可以不用再去实现线程的接口,不过还是要考虑一下队列满了的丢弃情况

List<ThreadEntity> listEntity = IntStream.range(0, 10).mapToObj(x -> ThreadEntity.builder().num(x).build()).collect(Collectors.toList());
List<CompletableFuture<Integer>> listCompletableFuture = listEntity.stream().map(x -> {
try {
// 此处ThreadPoolManager.getInstance().getPool()如果不传该参数则使用默认commonPool,无特殊需求的话trycatch一般不写
return CompletableFuture.supplyAsync(() -> x.countPrice(),
ThreadPoolManager.getInstance().getPool());
} catch (RejectedExecutionException e) {
System.out.println("reject" + x);
log.error("", e);
return null;
}
}).collect(Collectors.toList());
List<Integer> result = listCompletableFuture.stream().map(CompletableFuture::join).collect(Collectors.toList());
System.out.println(result);
System.out.println(listEntity);

输出如下可以看到运行是以多线程的方式进行,但是结果和原先是保持一致的

start6
start9
start0
start3
start2
start1
start8
start5
start4
start7
end3
end8
end5
end7
end9
end1
end2
end6
end0
end4
[131523688, 1491605535, 222657954, 132274662, 1134597171, 2057763841, 1168687436, 1842194861, 1264173480, 56446450]
[ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@7d6f201, num=0, price=131523688), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@58e825f3, num=1, price=1491605535), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@d458bb1, num=2, price=222657954), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@7e26830, num=3, price=132274662), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@43a0a2b8, num=4, price=1134597171), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@7aa70ac1, num=5, price=2057763841), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@45a8d047, num=6, price=1168687436), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@6dcdb8e3, num=7, price=1842194861), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@4b59d119, num=8, price=1264173480), ThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@35d5d9e, num=9, price=56446450)]

如果IntStream.range(0, 10)改成(0, 1000)则会有如下拒绝报错

java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.CompletableFuture$AsyncSupply@5af97850 rejected from java.util.concurrent.ThreadPoolExecutor@491666ad[Running, pool size = 64, active threads = 64, queued tasks = 256, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at java.util.concurrent.CompletableFuture.asyncSupplyStage(CompletableFuture.java:1618)
at java.util.concurrent.CompletableFuture.supplyAsync(CompletableFuture.java:1843)
at com.example.demo.lesson.grace.thread.TestMain.lambda$threadEx1$2(TestMain.java:34)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1384)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at com.example.demo.lesson.grace.thread.TestMain.threadEx1(TestMain.java:41)
at com.example.demo.lesson.grace.thread.TestMain.main(TestMain.java:26)
rejectThreadEntity(super=com.example.demo.lesson.grace.thread.ThreadEntity@1a9, num=366)

3.3.异步代码

以下代码可以直接简写成一行,在处理异步任务变得异常方便

CompletableFuture.runAsync(() -> fun())

List<ThreadEntity> listEntity = IntStream.range(0, 500).mapToObj(x -> ThreadEntity.builder().num(x).build()).collect(Collectors.toList());
List<CompletableFuture> listCompletableFuture = listEntity.stream().map(x -> {
try {
// 此处ThreadPoolManager.getInstance().getPool()如果不传该参数则使用默认commonPool,无特殊需求的话trycatch一般不写
return CompletableFuture.runAsync(() -> x.countPrice(), ThreadPoolManager.getInstance().getPool());
} catch (RejectedExecutionException e) {
System.out.println("reject" + x);
return null;
}
}).collect(Collectors.toList());
listCompletableFuture.stream().map(CompletableFuture::join);
System.out.println("1234");
// 一行多线程异步执行写法
CompletableFuture.runAsync(() -> System.out.println(1));

输出如下,可以看到主线程已经结束了其它子线程才在输出,完全没有等待的多线程

1234
1
start7
start0
start6
start5
start4
start2
start8
start1
start9
start3
end8
end4
end9
end6
end2
end0
end1
end3
end5
end7

【优雅代码】04-1行代码完成多线程,别再写runnable了的更多相关文章

  1. 【转】【翻】Android Design Support Library 的 代码实验——几行代码,让你的 APP 变得花俏

    转自:http://mrfufufu.github.io/android/2015/07/01/Codelab_Android_Design_Support_Library.html [翻]Andro ...

  2. Android Design Support Library 的 代码实验——几行代码,让你的 APP 变得花俏

    原文:Codelab for Android Design Support Library used in I/O Rewind Bangkok session--Make your app fanc ...

  3. Python入门-第一行代码到多行代码

    不管学啥语言,开始的第一行代码都是: print("hello word") 回车之后,就代表你正式进入代码的世界! 如果报错,恭喜你获得第一个书写bug,请检查单词拼写,双引号, ...

  4. 吴裕雄 Bootstrap 前端框架开发——Bootstrap 显示代码:多行代码带有滚动条

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  5. 用Python写一个随机数字生成代码,5行代码超简单

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 第一步,安装 random 库 random库是使用随机数的Python标准库 ...

  6. Html5游戏开发-145行代码完成一个RPG小Demo

    lufy前辈写过<[代码艺术]17行代码的贪吃蛇小游戏>一文,忽悠了不少求知的兄弟进去阅读,阅读量当然是相当的大.今天我不仿也搞一个这样的教程,目地不在于忽悠人,而在于帮助他人. 先看de ...

  7. 阿里 EasyExcel 7 行代码优雅地实现 Excel 文件生成&下载功能

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 资深架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

  8. 开源一个Mac漂亮的小工具 PPRows for Mac, 在Mac上优雅的计算你写了多少行代码

    开源一个Mac漂亮的小工具 PPRows for Mac, 在Mac上优雅的计算你写了多少行代码. 开源地址: https://github.com/jkpang/PPRows

  9. Android Studio 单刷《第一行代码》系列 04 —— Activity 相关

    前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...

随机推荐

  1. Vue API 3模板语法 ,指令

    条件# v-if# v-if 指令用于条件性地渲染一块内容.这块内容只会在指令的表达式返回 truthy 值的时候被渲染. v-show# v-show 指令也是用于根据条件展示一块内容.v-show ...

  2. MyBatis常用批量方法

    <!-- 批量添加派车单子表数据 --> <insert id="addBatch" parameterType="java.util.List&quo ...

  3. TCP协议三步挥手与四步挥手

    关于TCP协议 TCP(Transmission Control Protocol, 传输控制协议)是一种面向连接的.可靠的.基于字节流的传输层通信协议.与之对应的是UDP(User Datagram ...

  4. 可落地的DDD代码实践

    目录 前言 一.从六边形架构谈起 二.依赖倒置 三.DDD 代码分层 3.1 用户接口层 3.2 应用层 3.2 1 Response vs Exception 3.2.2 CQE vs DTO 3. ...

  5. OAuth2.0实战:认证、资源服务异常自定义!

    大家好,我是不才陈某~ 这是<Spring Security 进阶>的第4篇文章,往期文章如下: 实战!Spring Boot Security+JWT前后端分离架构登录认证! 妹子始终没 ...

  6. Jenkins凭证管理

    目录 一.简介 二.管理凭证 三.常用凭证 保密文本 账号密码 保密文件 账号秘钥 四.优雅使用凭证 保密文本 账号密码 保密文件 五.凭证插件 集成HashiCorp Vault pipeline ...

  7. Jenkins分布式与并行

    目录 一.简介 二.agent 通过JNLP协议增加agent 通过Swarm插件增加agent agent部分详解 三.agent放入Docker 使用Docker 配置Docker私有仓库 四.并 ...

  8. KNN分类

    1. KNN简介 K近邻(K-Nearest Neighbor)简称KNN.它可以做分类算法,也可以做回归算法.个人经验:KNN在做分类问题时非常有效. 2. KNN算法思想 在样本空间中,我们认为两 ...

  9. 30个类手写Spring核心原理之动态数据源切换(8)

    本文节选自<Spring 5核心原理> 阅读本文之前,请先阅读以下内容: 30个类手写Spring核心原理之自定义ORM(上)(6) 30个类手写Spring核心原理之自定义ORM(下)( ...

  10. CF1166A Silent Classroom 题解

    Content 现在有 \(n\) 名学生,我们需要将这些学生分到两个班上.对于两名在同一班级的学生,如果他们的名字首字母相同,他们就会聊天. 现在给定这些学生的名字,问最少有多少对学生会在一起聊天. ...