【优雅代码】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. Swagger2异常 java.lang.NumberFormatException: For input string: ""

    问题在访问swagger首页时报错: java.lang.NumberFormatException: For input string: "" at java.lang.Numb ...

  2. SpringBoot java配置类@Configuration 的两种写法

    首先在Springboot项目中,件一个java类,使用注解@Configuration  ,则这个类是SpringBoot bean的创建的配置文件类,,这种配置文件类有两种写法 1.使用包扫描 , ...

  3. Linux系统下部署eleasticsearch+kibana

    1.官网下载eleasticsearch和kibana,两个版本应安装一致,否则会出现kibana连接不上eleasticsearch的情况(这里我以6.3.1为例) eleasticsearch的下 ...

  4. shell 截取字符串实例教程

    本节内容:shell字符串截取方法 1,去掉字符串最左边的字符 [root@jbxue ~]$ vi test.sh 1 STR="abcd" 2 STR=${STR#" ...

  5. Alamofire-5.0.0 以上报错

    摘要 Alamofire 更新到新版本时,遇到了两个错误和一个警告️,所以记录下来它们,以及如何解决它们.给其他出现类似问题的同道一些解决的方向. 今天新开启一个项目,因为网络请求选择 Alamofi ...

  6. gitlab 备份&恢复

    Gitlab 成功运行起来之后,最终的事情就是定期的备份,遇到问题后的还原. 备份配置 默认 Gitlab 的备份文件会创建在/var/opt/gitlab/backups文件夹中,格式为时间戳_日期 ...

  7. LuoguP7072 [CSP-J2020] 直播获奖 题解

    Update \(\texttt{2020.11.13}\) 修改了一个小细节. \(\texttt{2020.11.16}\) 修改了一个错误. Content 有一场 \(n\) 个人的比赛,计划 ...

  8. ymal文档格式 处理

    Python也可以很容易的处理ymal文档格式,只不过需要安装一个模块. 参考文档:http://pyyaml.org/wiki/PyYAMLDocumentation

  9. jquery autocomplete 插件的使用

    Autocomplete 自动完成 功能:输入框在输入关键字后,根据输入的内容给出相关的下拉框供用户选择,自动完成输入内容. 插件:使用jqueryUI的自动完成小部件,文档地址:https://jq ...

  10. Intellij IDEA添加插件

    1. Lombok插件 IDEA增加对Lombok的支持 <!--lombok插件--> <dependency> <groupId>org.projectlomb ...