java虚拟线程

什么是虚拟线程?

虚拟线程就是其他语言中的协程(比如go)。可以简单理解为一种用户态线程,和操作系统线程的关系由原来的1对1,变成M(虚拟线程)对N(
os线程)的关系。

为什么要有虚拟线程?

  • os不知道用户态如何使用线程,所以jvm会分配一个默认的大小(可以通过-XX:ThreadStackSize修改),通常会比实际使用的要大很多,也就导致在cup或者网络连接耗尽之前
    线程数量先达到上线。
  • 线程上下文切换要通过内核调度进行,相对更慢;

os线程的开销限制了java程序不能创建太多的线程,导致硬件资源利用不够。其次再java使用线程需要设置线程池
来管理线程,不如go协程使用简单。

一些解决方案

1.使用异步

放弃单个请求单个线程的吃力方式,而是在等待io操作时,将线程还给线程池,以便线程可以为其他请求提供服务。
这种细粒度的线程共享(代码仅在执行计算时(而不是在等待 I/O 时)保留线程,允许大量并发操作,而不会消耗大量线程。
不等待io,而是回调通知。比如CompletableFuture或者所谓的响应式框架。好处是请求的每个阶段都可以在不用的
线程上执行,坏处就是失去了顺序执行,提高的调试的难度。

2.使用虚拟线程

正如操作系统将虚拟地址空间映射到有限数量的物理地址空间,给人一种内存充足的错觉。java运行时可以通过将大量的虚拟内存映射到少量的操作系统线程
来给人一种线程充足的错觉。当虚拟线程使用cpu执行计算时才使用os线程,当执行阻塞io操作时运行时将执行非阻塞系统调用。并自动挂起虚拟线程,直到以后
可以恢复。

虚拟线程的含义

虚拟线程的成本非常低,所以永远不应该被池化。应该为每个应用程序任务分配一个新的虚拟线程。因此,大多数虚拟线程的生存期很短。并且调用堆栈浅。

img_8.png

虚拟线程真正擅长的是等待,等待大量阻塞操作完成。它能提供的是 scale(更高的吞吐量),而不是 speed(更低的延迟)。
虚拟线程最适合的是原来需要更多线程数来处理计算无关业务的场景,典型的就是像web容器、数据库、文件操作一类的IO密集型的应用。

虚拟线程的调用堆栈存在java堆上,而不是os分配的栈区域。

虚拟线程的运行实际上就是两个操作

  • 挂载(mount):挂载虚拟线程意味着将所需的栈帧从堆中临时复制到载体线程的堆栈中,并在挂载时借用载体堆栈执行。
img.png

卸载(unmount):当在虚拟线程中运行的代码因为 IO、锁等原因阻塞后,它可以从载体线程中卸载,然后将修改的栈帧复制回堆中,从而释放载体线程以进行其他操作(例如运行另一个虚拟线程)。对应的,JDK
中几乎所有的阻塞点都已经过调整,因此当在虚拟线程上遇到阻塞操作时,虚拟线程会从其载体上卸载而不是阻塞。

img_1.png

scheduler:可以使用ForkJoinPool可以用。work-stealing + FIFO,性能很好。

img_2.png

JDK中的一些阻塞操作不会卸载虚拟线程,因此会阻塞其载体线程。这是因为操作系统级别(例如,许多文件系统操作)或JDK级别(例如,Object.wait()
)的限制。这些阻塞操作的解决方式是,通过临时扩展scheduler的并行性来补偿操作系统线程的捕获。因此,scheduler的ForkJoinPool中的平台线程数量可能暂时超过CPU核数。

如何使用?

1.简单的使用

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 10_000).forEach(i -> {
        executor.submit(() -> {
            Thread.sleep(Duration.ofSeconds(1));
            return i;
        });
    });
}  // executor.close() is called implicitly, and waits

在这种情况下提高虚拟线程数,就可以提高吞吐量。但是如果任务是一个巨大计算任务,比如排序,那么超出cpu核数的线程数将毫无作用。

换句话说,虚拟线程可以在以下情况下显著提高应用程序吞吐量

  • 并发任务的数量很多(超过几千个),并且工作负载不受 CPU 限制,因为在这种情况下,线程数比处理器内核多得多,无法提高吞吐量。

2.请求合并

handleExecutorService

void handle(Request request, Response response) {
    var url1 = ...
    var url2 = ...
 
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        var future1 = executor.submit(() -> fetchURL(url1));
        var future2 = executor.submit(() -> fetchURL(url2));
        response.send(future1.get() + future2.get());
    } catch (ExecutionException | InterruptedException e) {
        response.fail(e);
    }
}
 
String fetchURL(URL url) throws IOException {
    try (var in = url.openStream()) {
        return new String(in.readAllBytes(), StandardCharsets.UTF_8);
    }
}

比如这样做请求合并

3.使用信号量限制虚拟线程,而不是固定大小的线程池

因为,线程池只能限制os线程的数量,而无法限制协程数量。

4.如何判断一个thread是否是虚拟线程

Thread.isVirtual()

什么时候虚拟线程会被绑定到具体os。

  1. 当它在块或方法内执行代码时,或者synchronized
  2. 当它执行一个方法或一个外来函数时。native

限制

  1. 虚拟线程的当前限制是 G1 GC 不支持大的堆栈块对象。如果虚拟线程的堆栈达到区域大小的一半(可能小至
    512KB),则可能会抛出StackOverflowError。
  2. 虚拟线程始终是守护线程,Thread.setDaemon(boolean)方法不能将虚拟线程更改为非守护程序线程。

Scoped Values (Third Preview)(等正式版再补充)

ThreadLocal设计的缺陷

  1. 无可变性约束。
  2. 无限生存期。
  3. 通过使用 InheritableThreadLocal,子线程可以继承父线程的线程局部变量
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容