Loom 问答
Ron Pressler,2020 年 7 月 15 日《Loom 状态》的发布引发了一些常见问题。以下是我的尝试回答其中一些问题。
Loom 只是将线程小型化;这是否意味着我们仍然会遇到与今天线程相同的问题,只是更多而已?
线程的一些最大问题是因为它们是昂贵的、有限的资源,必须共享并仔细管理。我们可以“调整”线程以获得每个任务一个线程的关联,这本身就解决了线程的许多最大问题,例如池管理、线程本地泄漏和复杂的中断。因此,虽然虚拟线程“仅仅”是小型化的线程,但小型化极大地改变了我们思考和使用构造的方式。智能手机可以被认为是小型化的大型机,但它没有带来多用户、始终在线机器所需的管理问题。确实,拥有数百万个线程的能力可能会引入新的问题,但线程现在可以完全与逻辑操作相关联,这提供了以业务逻辑为中心的简单管理方法。这就是结构化并发的目标。
Loom 带来的好处之一是,虚拟线程被各种可服务性和可观察性工具(如调试器和探查器(JFR))识别,并用作其“上下文单元”。我们不能让平台了解其他类型的任务上下文,比如响应式流吗?
这将是抽象的错误使用。线程已经被识别为基本上下文单元。虚拟线程足够轻量级,可以为即使是最小、最短命的任务提供上下文,因此无需再添加另一种上下文。线程抽象对我们很有用;到目前为止,问题在于实现。
我的应用程序受 CPU/数据库容量的限制,Loom 对此无济于事,那么有什么意义呢?
Loom 无法变出硬件资源,它对根本没有使用它的软件组件没有任何影响。它以自然、透明和细粒度的方式促进对可用资源的更好利用。例如,与其管理多个大小不同的线程池,不如获取一个信号量,在该信号量中使用有限的资源(如数据库);不使用数据库的线程不会受到影响,而使用数据库的线程将通过一个集中式、易于观察的机制进行协调。
其他语言中提供的异步/等待提供的协作式调度允许我确切地知道我的并发调度点在哪里。这难道不是比依赖线程抽象更好的编写并发代码的方式吗?
否。首先,Java 平台已经拥有抢占式线程,因此协作式调度只会除了基于线程的并发之外再添加另一种并发,而且是与之不兼容的并发。我们不是解决了一个问题,而是两个问题。
其次,即使在没有提供抢占式调度的平台上(如 JavaScript),协作式调度也是一种较差的解决方案,尤其是在任务可能以多种方式相互交互的情况下,而不是仅仅共享 UI。可以将抢占式调度和协作式调度之间的区别视为对不同默认值的选择。使用协作式调度,默认情况下,每个操作都在一个临界区内进行,在该临界区内不允许其他任务的操作交织,我们明确标记允许交织发生的点,而使用抢占式调度,交织可以在任何地方发生,除了我们使用锁保护的临界区明确禁止的地方。在大多数服务器端使用中,后一种默认值更可取,因为大多数操作都是有限的并且对调度点不敏感,因为锁允许针对特定共享资源的临界区。但最重要的是,当我们明确标记临界区时,我们的代码在面对更改时更加健壮。在协作式调度的情况下,在子例程中添加调度点(通常是必要的,以允许它进行 I/O)可能会破坏其所有传递调用者中重要但隐式的假设。