Java21虚拟线程实践

简介: 虚拟线程是一种轻量化的线程封装,由jvm直接调度和管理。反之普通的线程其实是调用的操作系统的能力,对应的是操作系统级的线程。相对虚拟线程来说操作系统级的线程持有成本很高,而且受操作系统调度和管理的。实际在普通多线程情况下,如果出现IO阻塞,这个线程就必须得跟着阻塞,这个线程对应的操作系统就被阻塞,而他却持有大量的内存。另外,要处理大量的IO就得新建更多线程,而大量的线程会在操作系统调度时因上下文切换导致大量的CPU被浪费。

就在前几天,java21正式版发布了,作为继java17之后的又一个长期支持版本 (LTS),为我们带来了很多新的特性,其中我最感兴趣的就是虚拟线程(virtual thread),相信大家对虚拟线程也很好奇。趁着空闲时间安装了jdk21来体验一把,顺便把我查到的关于java21虚拟线程相关的资料也分享下。

虚拟线程的使用

 首先来看下虚拟线程怎么使用,jdk21在Thread类中,专门提供了虚拟线程和虚拟线程工厂的创建入口,我们挨个看下。首先就是虚拟线程的创建和启动,使用lambda也就几行代码:


 

Thread.ofVirtual().start(() -> {
            System.out.println("Hello, virtual thread!");
        });
        // 也可以指定虚拟线程的名字
        Thread.ofVirtual().name("virtual thread").start(() -> {
            System.out.println("Hello, virtual thread!");
        });


 Thread也提供了虚拟线程工厂,有了虚拟线程工厂,我们就可以在ExecutorService中使用虚拟线程。当然Executors已经提供好了封装,我们直接调用即可:


   

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


 可以看得出来,虚拟线程几乎没有啥使用门槛,那他到底和普通线程有啥区别?我在查阅了一些资料后,我的理解如下:(可能理解浅薄或者有些错误,请指正)


什么是虚拟线程

 虚拟线程是一种轻量化的线程封装,由jvm直接调度和管理。反之普通的线程其实是调用的操作系统的能力,对应的是操作系统级的线程。相对虚拟线程来说操作系统级的线程持有成本很高,而且受操作系统调度和管理的。实际在普通多线程情况下,如果出现IO阻塞,这个线程就必须得跟着阻塞,这个线程对应的操作系统就被阻塞,而他却持有大量的内存。另外,要处理大量的IO就得新建更多线程,而大量的线程会在操作系统调度时因上下文切换导致大量的CPU被浪费。


 如果我们能在某个普通线程在等待IO返回的情况下,让其运行其他的任务,是不是就可以用少量的线程处理大量的IO?思路很美好,那具体怎么实施呢!在计算机科学领域,解决问题最简单的方式就是加一层,比如操作系统中,代码访问内存中间就有一层虚拟内存,如果代码到线程中间加一层虚拟线程,每个虚拟线程只有在真正需要CPU运行的时候,才会被映射到真正的线程上去运行,而IO阻塞时会换其他非阻塞的虚拟线程上来,这样就不需要创建大量的线程了,而虚拟线程只需要持有少量的上下文信息即可。


 这种实现方式带来了很多优势,比如:


轻量级:虚拟线程占用内存更少,创建和切换代价更低。

支持异步:虚拟线程支持异步非阻塞编程模型。

扩展性好:可以在少量线程上运行大量虚拟线程。

无上下文切换:协程在同一线程中运行,没有线程上下文切换。

虚拟线程和协程

 Java21实际上在实现虚拟线程时,兼容了普通线程(不确定是否完全兼容),像ThreadLocal、Semaphore之类的工具完全可以在虚拟线程中使用,基本上大部分使用线程的地方应该都可以替换成虚拟线程,也就是说以后可以肆无忌惮创建虚拟线程而不用担心过多创建线程了。以上的内容看起来很像是go或者python中的协程,但在medium上看到一篇文章,解释了Java中的虚拟线程和协程之间的异同,摘抄下来方便大家更深入理解,如果有兴趣也可以去看原文。


相同之处:

虚拟线程和协程都很轻量级,它们的创建和销毁开销小于传统的操作系统线程。

虚拟线程和协程都可以通过暂停和恢复在线程之间切换,从而避免线程上下文切换的开销。

虚拟线程和协程都可以以异步和非阻塞的方式处理任务,提高应用程序性能和响应速度。

不同之处:

虚拟线程在JVM级别实现,而协程在语言级别实现。因此,虚拟线程的实现可以用于任何支持JVM的语言,而协程的实现需要特定编程语言的支持。

虚拟线程是协程的基于线程的实现,因此可以使用线程相关的API,如ThreadLocal,Lock和Semaphore。协程不依赖于线程,通常需要特定的异步编程框架和API。

虚拟线程的调度由JVM管理,而协程的调度由编程语言或异步编程框架管理。因此,虚拟线程可以更好地与其他线程合作,而协程更适合处理异步任务。

总结

 有了虚拟线程,我们可以用虚拟线程替换许多使用线程的场景,任何需要异步或者多线程运行的情况下,我们都直接直接扔给虚拟线程去运行,丝毫不用顾虑的过度创建线程的问题,但这里需要额外注意,对于CPU密集型的任务多线程或者多虚拟线程依旧是无法提升性能的。虚拟线程是否能完全替代普通线程,这点肯定是不可能的,比较很多时候还是需要操作系统去做任务调度的,而目前操作系统最小的调度单位依旧是线程。


 总之,Java21正式推迟了虚拟线程,我相信在很多高IO的场景下肯定可以提升性能的,至于具体能提升多少,还是有待于具体数据的。 最后用两句老梗来结束本篇文章。

目录
相关文章
|
15天前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
72 17
|
26天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
11天前
|
缓存 安全 算法
Java 多线程 面试题
Java 多线程 相关基础面试题
|
28天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
28天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
28天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
55 3
|
28天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
157 2
|
8月前
|
存储 安全 Java
深入理解Java并发编程:线程安全与锁机制
【5月更文挑战第31天】在Java并发编程中,线程安全和锁机制是两个核心概念。本文将深入探讨这两个概念,包括它们的定义、实现方式以及在实际开发中的应用。通过对线程安全和锁机制的深入理解,可以帮助我们更好地解决并发编程中的问题,提高程序的性能和稳定性。
|
5月前
|
存储 安全 Java
解锁Java并发编程奥秘:深入剖析Synchronized关键字的同步机制与实现原理,让多线程安全如磐石般稳固!
【8月更文挑战第4天】Java并发编程中,Synchronized关键字是确保多线程环境下数据一致性与线程安全的基础机制。它可通过修饰实例方法、静态方法或代码块来控制对共享资源的独占访问。Synchronized基于Java对象头中的监视器锁实现,通过MonitorEnter/MonitorExit指令管理锁的获取与释放。示例展示了如何使用Synchronized修饰方法以实现线程间的同步,避免数据竞争。掌握其原理对编写高效安全的多线程程序极为关键。
81 1
|
6月前
|
安全 Java 开发者
Java并发编程中的线程安全问题及解决方案探讨
在Java编程中,特别是在并发编程领域,线程安全问题是开发过程中常见且关键的挑战。本文将深入探讨Java中的线程安全性,分析常见的线程安全问题,并介绍相应的解决方案,帮助开发者更好地理解和应对并发环境下的挑战。【7月更文挑战第3天】
118 0