Java之协程(quasar)
一、前面我们简单的说了一下,Python中的协程原理。这里补充Java的协程实现过程。有需要可以查看python之协程。
二、Java协程,其实做Java这么久我也没有怎么听过Java协程的东西,但是一直有有听到微线程/协程的概念,这不在学习Python的时候接触到了协程一词。然后返回来去了解Java的协程问题,但是看了很多资料,发现官网以及很多地方都没有涉及到协程的东西,没有办法,只能通过强大的社区来学习协程的相关东西。
三、这里主要关注的是:quasar。
1)协程的目的:当我们在使用多线程的时候,如果存在长时间的I/O操作。这个时候线程一直处于阻塞状态,如果线程很多的时候,会存在很多线程处于空闲状态,造成了资源应用不彻底。相对的协程不一样了,在单线程中多个任务来回自行如果出现长时间的I/O操作,让其让出目前的协程调度,执行下一个任务。当然可能所有任务,全部卡在同一个点上,但是这只是针对于单线程而言,当所有数据正常返回时,会同时处理当前的I/O操作。
2)多线程测试(这里使用100万个线程,来测试内存占用)
for (int i = 0; i < 1000000; i++) {
new Thread(() -> {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
结果:
直接卡死了,内存溢出
可想而知,如果存在100万个线程,开销是有多大。
3)协程测试
a、阿里云搜索到的依赖包
<dependency>
<groupId>co.paralleluniverse</groupId>
<artifactId>quasar-core</artifactId>
<version>0.7.9</version>
<classifier>jdk8</classifier>
</dependency>
b、测试内存占用量
public static void main(String[] args) throws Exception {
//使用阻塞队列来获取结果。
LinkedBlockingQueue<Fiber<Integer>> fiberQueue = new LinkedBlockingQueue<>();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
for (int i = 0; i < 1000000; i++) {
int finalI = i;
//这里的Fiber有点像Callable,可以返回数据
Fiber<Integer> fiber = new Fiber<>((SuspendableCallable<Integer>) () -> {
//这里用于测试内存占用量
Fiber.sleep(100000);
System.out.println("in-" + finalI + "-" + LocalDateTime.now().format(formatter));
return finalI;
});
//开始执行
fiber.start();
//加入队列
fiberQueue.add(fiber);
}
while (true) {
//阻塞
Fiber<Integer> fiber = fiberQueue.take();
System.out.println("out-" + fiber.get() + "-" + LocalDateTime.now().format(formatter));
}
}
结果:
堆:
估计:1个G左右。
内存:
估计:1个G左右,也就是每一个fiber占用1Kb左右。
c、正常测试
修改一下参数:
public static void main(String[] args) throws Exception {
//使用阻塞队列来获取结果。
LinkedBlockingQueue<Fiber<Integer>> fiberQueue = new LinkedBlockingQueue<>();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
for (int i = 0; i < 10; i++) {
int finalI = i;
//这里的Fiber有点像Callable,可以返回数据
Fiber<Integer> fiber = new Fiber<>((SuspendableCallable<Integer>) () -> {
//这里用于测试内存占用量
Fiber.sleep(1000);
System.out.println("in-" + finalI + "-" + LocalDateTime.now().format(formatter));
return finalI;
});
//开始执行
fiber.start();
//加入队列
fiberQueue.add(fiber);
}
while (true) {
//阻塞
Fiber<Integer> fiber = fiberQueue.take();
System.out.println("out-" + fiber.get() + "-" + LocalDateTime.now().format(formatter));
}
}
结果:
4)可以看出并发的状态还是很不错的,当然这只是多个任务执行而已。
四、通过上面的测试,可以看出,quasar中Fiber,很像Callable的用法、而且在内存占用上面减少了很多,当然,堆的数量确实不少,但是可以接受。
还是要说明:协程的方式更多用来做I/O密集型的操作。计算密集型的还是使用线程更加合理。
五、原理,我估么着看了一下源码,复杂度很高,这里不做深究。
1、Quasar里的Fiber其实是一个continuation,他可以被Quasar定义的scheduler调度,一个continuation记录着运行实例的状态,而且会被随时中断,并且也会随后在他被中断的地方恢复。Quasar其实是通过修改bytecode来达到这个目的,所以运行Quasar程序的时候,你需要先通过java-agent在运行时修改你的代码,当然也可以在编译期间这么干。golang的内置了自己的调度器,Quasar则默认使用ForkJoinPool这个JDK7以后才有的,具有work-stealing功能的线程池来当调度器。work-stealing非常重要,因为你不清楚哪个Fiber会先执行完,而work-stealing可以动态的从其他的等等队列偷一个context过来,这样可以最大化使用CPU资源。
2、那这里你会问了,Quasar怎么知道修改哪些字节码呢,其实也很简单,Quasar会通过java-agent在运行时扫描哪些方法是可以中断的,同时会在方法被调用前和调度后的方法内插入一些continuation逻辑,如果你在方法上定义了@Suspendable注解,那Quasar会对调用该注解的方法做类似下面的事情。
3、这里假设你在方法f上定义了@Suspendable,同时去调用了有同样注解的方法g,那么所有调用f的方法会插入一些字节码,这些字节码的逻辑就是记录当前Fiber栈上的状态,以便在未来可以动态的恢复。(Fiber类似线程也有自己的栈)。在suspendable方法链内Fiber的父类会调用Fiber.park,这样会抛出SuspendExecution异常,从而来停止线程的运行,好让Quasar的调度器执行调度。这里的SuspendExecution会被Fiber自己捕获,业务层面上不应该捕获到。如果Fiber被唤醒了(调度器层面会去调用Fiber.unpark),那么f会在被中断的地方重新被调用(这里Fiber会知道自己在哪里被中断),同时会把g的调用结果(g会return结果)插入到f的恢复点,这样看上去就好像g的return是f的local variables了,从而避免了callback嵌套。
4、上面啰嗦了一大堆,其实简单点讲就是,想办法让运行中的线程栈停下来,好让Quasar的调度器介入。JVM线程中断的条件只有两个,一个是抛异常,另外一个就是return。这里Quasar就是通过抛异常的方式来达到的,所以你会看到我上面的代码会抛出SuspendExecution。但是如果你真捕获到这个异常,那就说明有问题了,所以一般会这么写。
Java之协程(quasar)的更多相关文章
- 异步时代-java的协程路在何方
面试官:你知道协程吗? 你:订机票的那个吗,我常用. 面试官:行,你先回去吧,到时候电话联系 ........ 很尴尬,但是事实是,很大一部分的程序员不知道协程是啥玩意,更大一部分的程序员,项目中没用 ...
- 第二章 - Java与协程
Java与协程 内核线程的局限 通过一个具体场景来解释目前Java线程面临的困境.今天对Web应用的服务要求,不论是在请求数量上还是在复杂度上,与十多年前相比已不可同日而语,这一方面是源于业务量的增长 ...
- Java协程实践指南(一)
一. 协程产生的背景 说起协程,大多数人的第一印象可能就是GoLang,这也是Go语言非常吸引人的地方之一,它内建的并发支持.Go语言并发体系的理论是C.A.R Hoare在1978年提出的CSP(C ...
- 协程,纤程(Fiber),或者绿色线程(GreenThread)
纤程(Fiber),或者绿色线程(GreenThread) 面试官:你知道协程吗? 你:订机票的那个吗,我常用. 面试官:行,你先回去吧,到时候电话联系 ........ 很尴尬,但是事实是,很大一部 ...
- JAVA协程 纤程 与Quasar 框架
ava使用的是系统级线程,也就是说,每次调用new Thread(....).run(),都会在系统层面建立一个新的线程,然鹅新建线程的开销是很大的(每个线程默认情况下会占用1MB的内存空间,当然你愿 ...
- Java不支持协程?那是你不知道Quasar!
原创:微信公众号 码农参上,欢迎分享,转载请保留出处. 在编程语言的这个圈子里,各种语言之间的对比似乎就一直就没有停过,像什么古早时期的"PHP是世界上最好的语言"就不提了,最近我 ...
- java 协程
协程是比线程更轻量级的程序处理单元,也可以说是运行在线程上的线程,由自己控制 1.适用于被阻塞的,且需要大量并发的场景. 2.不适用于,大量计算的多线程,遇到此种情况,更好实用线程去解决. 虽然Jav ...
- 都2019年了,Java为什么还在坚持多线程不选择协程?
都2019年了,Java为什么还在坚持多线程不选择协程? - 知乎 https://www.zhihu.com/question/332042250/answer/734051666
- 深入分析 Java、Kotlin、Go 的线程和协程
前言 协程是什么 协程的好处 进程 进程是什么 进程组成 进程特征 线程 线程是什么 线程组成 任务调度 进程与线程的区别 线程的实现模型 一对一模型 多对一模型 多对多模型 线程的"并发& ...
随机推荐
- hive匹配中文
select regexp_extract('ab中文123测试55..', '[\u4e00-\u9fa5]+', 0) 只提出成功第一段中文汉字,结果为: 中文 select regexp_rep ...
- 沃顿商学院的MBA课程
沃顿商学院的MBA课程,分为必修课和选修课两部分 (一)必修课: 1.领导力:团队合作和领导力的基础 2.营销学:营销管理 3.微观经济学:微观经济基础 4.经济学:管理经济学的高级话题 5.统计学: ...
- word: 插入或修改文字时后面的字消失 解决办法
在编辑Word文档中的文字时,我们有时需要插入或修改文字,可是在插入或修改时会发现改动处后面的文字会消失.比如插入或修改3个字,后面的文字随之也会消失3个,这时该怎么办呢? 点击-“文件”-“选项”- ...
- linux内核在哪里处理设备树中compatible为"syscon"的节点?
答: linux内核源码drivers/mfd/syscon.c中的of_syscon_register()接口对regmap_config进行初始化 注: linux内核源码版本为5.1.0
- Nessus更新到8.5.0
Nessus更新到8.5.0 此次更新,主要涉及以下变化: (1)Nessus的用户注册和激活流程进行简化.用户可以在Nessus软件中直接进行注册和激活. (2)Nessus报告生成功能得到加强 ...
- 深度学习:21天实战caffe学习资源-4-环境安装
使用anaconda3环境下的python2.7, 机器macos mojave 10.14 1.安装Xcode 首先现在app store中安装Xcode: 不然会有” framework not ...
- hugepage设置相关总结
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/shaoyunzhe/article/de ...
- Python初级 2 记住内存和变量的练习
一.数据类型: 数字:3, 5, 100, 50.35 字符串:"abc","wang" 字符串或数字可以由名字来表示,名字也叫变量 二.算术表达式: 形如3 ...
- extends 类的继承 / super关键字,调用继承类里面的函数和变量
Son 继承Father 当其他脚本想调用 Father类里面的变量 or 方法 可以把 Son r=new Son() 等价于 Father r=new Father() 注意: 函数只能单继承 ...
- python中异常处理之esle,except,else
异常是指程序中的例外,违例情况.异常机制是指程序出现错误后,程序的处理方法.当出现错误后,程序的执行流程发生改变,程序的控制权转移到异常处理. python中使用try...except语句捕获异常, ...