首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >究竟是什么使Java虚拟线程更好

究竟是什么使Java虚拟线程更好
EN

Stack Overflow用户
提问于 2022-05-04 16:48:24
回答 3查看 989关注 0票数 12

我对“织机计划”很感兴趣,但有一件事我不能完全理解。

大多数Java服务器使用的线程池都有一定的线程限制(200,300 .),但是,您不会受到操作系统的限制而产生更多的线程池,我已经了解到,对于Linux的特殊配置,您可以达到巨大的数量。

OS线程更昂贵,启动/停止速度更慢,必须处理上下文切换(被它们的数量放大),并且您依赖于操作系统,它可能拒绝为您提供更多的线程。

话虽如此,虚拟线程也消耗了类似数量的内存(至少这是我所理解的)。有了织机,我们可以得到尾部调用优化,这将减少内存的使用。此外,同步和线程上下文复制仍然是一个类似大小的问题。

事实上,你能够产生数百万的虚拟线程

代码语言:javascript
复制
public static void main(String[] args) {
    for (int i = 0; i < 1_000_000; i++) {
        Thread.startVirtualThread(() -> {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
}

当我使用平台线程时,上面的代码在25k左右中断,OOM异常。

我的问题是,究竟是什么使这些线程变得如此轻,是什么阻止我们生成100万个平台线程并与它们一起工作,仅仅是上下文切换使常规线程变得如此“沉重”。

一个非常相似的问题

到目前为止我发现的东西:

  • 上下文切换是昂贵的。一般来说,即使在操作系统知道线程将如何运行的理想情况下,如果每个线程具有相同的优先级,它仍然必须给每个线程相同的执行机会。如果我们产生了10k的OS线程,它将不得不不断地在它们之间切换,而在某些情况下,仅这个任务就可以占用80%的CPU时间,所以我们必须非常小心处理这些数字。使用虚拟线程,上下文切换由JVM完成,这使得它基本上是免费的
  • 廉价启动/停止。当我们中断一个线程时,我们实际上告诉任务,“杀死正在运行的OS线程”。但是,如果该线程位于线程池中,则在我们请求的时候,线程可能会被当前任务释放,然后分配给另一个线程,而另一个任务可能会得到中断信号。这使得中断过程相当复杂。虚拟线程只是堆中的对象,我们可以让GC在后台收集它们。
  • 由于操作系统处理线程的方式,硬上限限制了(最多数万个线程)。操作系统不能调整到特定的应用程序和编程语言,所以它必须为最坏的情况--内存--做好准备。它必须分配更多的内存,这些内存将实际用于满足所有需求。在进行所有这些工作时,它必须确保重要的OS进程仍然工作。使用VT,您只受到内存的限制,这是很便宜的。
  • 执行事务的线程的行为与执行视频处理的线程非常不同,同样,操作系统必须为最坏的情况做好准备,并尽可能地兼顾这两种情况,这意味着我们在大多数情况下性能不佳。由于VT是由Java本身生成和管理的,因此可以完全控制它们和不绑定到OS的特定任务优化。
  • 可调整大小的堆栈。操作系统为线程提供了一个适合所有用例的大堆栈,Virtual有一个驻留在堆空间中的可调整大小的堆栈,它被动态地调整大小以适应问题,从而使其更小
  • 更小的元数据大小。平台线程使用1MB,而虚拟线程需要200到300个字节来存储它们的元数据。
EN

回答 3

Stack Overflow用户

发布于 2022-09-08 16:23:52

coroutines (所以是虚拟线程)的一个巨大优点是,它们可以生成高级别的并发性,而无需回调的缺点。

首先让我介绍一下利特尔定律:

代码语言:javascript
复制
concurrency = arrival_rate * latency

我们可以把这个改写成:

代码语言:javascript
复制
arrival_rate = concurrency/latency

在稳定的系统中,到达率等于吞吐量。

代码语言:javascript
复制
throughput = concurrency/latency

要提高吞吐量,您有两个选项:

  1. 减少延迟;这通常非常困难,因为您对远程调用或磁盘请求所花费的时间几乎没有影响。
  2. 增加并发性

对于常规线程,由于上下文切换开销,阻塞调用很难达到高度并发性。在某些情况下,可以异步发出请求(例如NIO + Epoll或Netty io_uring绑定),但是您需要处理回调和回调地狱。

使用虚拟线程,可以异步发出请求,并将虚拟线程停放并调度另一个虚拟线程。一旦接收到响应,虚拟线程就会被重新安排,这将完全透明地完成。编程模型比使用经典线程和回调要直观得多。

票数 4
EN

Stack Overflow用户

发布于 2022-09-27 07:34:55

虚拟线程封装在平台线程上,因此您可能会认为它们是JVM提供的错觉,整个想法是将线程的生命周期设置为 CPU界 操作。

究竟是什么让Java虚拟线程更好呢?

虚拟线程优势

  • 显示与平台线程完全相同的行为。
  • 一次性的,可以被缩放到数百万。
  • 比平台线程轻量级得多。
  • 创建时间快,就像创建字符串对象一样快。
  • JVM对IO操作进行分隔,对于虚拟线程没有IO。
  • 但是可以像以前那样有顺序的代码,但是要有效得多。
  • JVM给人一种虚拟线程的错觉,整个故事下面是在平台线程上。
  • 随着虚拟线程CPU内核的使用变得更加并发,虚拟线程和多核CPU与ComputableFutures的结合对于并行化代码来说是非常强大的。

虚拟线程使用注意事项

  • 不要使用监视器,即同步块,但是这将在JDK的新版本中修复,另一种方法是使用“ReentrantLock”和try-final语句。
  • 在堆栈上阻塞本地帧,JNI‘。它非常罕见。
  • 每个堆栈的控制内存(减少线程区域设置,不进行深度递归)
  • 尚未更新的监视工具,如调试器、JConsole、VisualVM等
  • 平台线程与虚拟线程。平台线程在基于IO的任务和操作中将OS线程劫持为人质,仅限于线程池和OS线程中适用的线程数,默认情况下它们不是守护进程线程。
  • 虚拟线程是用JVM实现的,在CPU绑定操作中,与平台线程相关联并将它们重新组合到线程池中,在IO绑定操作完成后,将从线程池调用一个新线程,因此在本例中不存在任何问题。

第四层架构有更好的理解.

CPU

  • 多核CPU多核,具有cpu执行操作。

操作系统

  • OS线程OS调度程序将cpu时间分配给正在使用的OS线程。

JVM

  • 平台线程被完全包装在OS线程上,具有两个任务操作。
  • 在每个CPU绑定操作中,虚拟线程与平台线程相关联,每个虚拟线程可以在不同的时间与多个平台线程相关联。

带有可执行服务的虚拟线程

  • 使用executer服务更有效,因为它与线程池相关联--仅限于适用的线程池,但是在虚拟线程、Executer服务和虚拟包含的比较中,我们不使用ned来处理或管理关联的线程池。 尝试( service.submit(ExecutorServiceVirtualThread::taskOne);服务= Executors.newVirtualThreadPerTaskExecutor()) {ExecutorService service.submit(ExecutorServiceVirtualThread::taskTwo);}
  • Executer服务在JDK 19中实现了自动关闭接口,因此,当它与'try with resource‘一起使用时,一旦它到达'try’块的末尾,调用'close‘api,或者主线程将等待所有提交的任务与其专用的虚拟线程一起完成其生命周期,并关闭相关的线程池。 service.submit(ExecutorServiceThreadFactory::taskOne);工厂=Thread.ofVirtual().name(“用户线程-”,0).factory();try(ExecutorService service =Executors.newThreadPerTaskExecutor(工厂)){ ThreadFactory service.submit(ExecutorServiceThreadFactory::taskTwo);}
  • 也可以使用虚拟线程工厂创建Executer服务,只需将线程工厂与其构造函数参数放在一起即可。
  • 可以受益于执行服务的特点,如未来和可完成的未来。

查找有关杰普-425的更多信息

票数 1
EN

Stack Overflow用户

发布于 2022-10-06 10:32:30

有时,人们不得不构建能够同时处理大量客户端的系统。由于RAM消耗和上下文切换成本,本机线程无法满足这一要求。

虚拟线程使我们能够同时运行数以百万计的I/O绑定任务,而无需改变我们的心智模型。

这就是戈朗进入这个行业的原因(除了谷歌的支持)。Goroutines是一个非常类似于Java虚拟线程的概念,它们解决了同样的问题。

还有其他方法来实现虚拟线程所做的事情(例如NIO和相关的反应堆模式)。然而,这就需要使用消息循环和回调来扭曲您的思维(这就是为什么这么多人讨厌JavaScript)。在它们上面有几层抽象,使事情变得简单一些,但它们也有代价。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72116652

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档