Java中的虚拟线程旨在提高并发编程的效率和性能,通过允许创建数以百万计的线程而对系统资源的消耗最小。虚拟线程背后的主要思想是将线程的调度任务从操作系统级别转移到Java虚拟机(JVM)级别,从而减少上下文切换的成本并提高系统的吞吐量。
虚拟线程是在JEP425中提出的,但是JEP425在介绍他的用法时,特意提到了一个比较关键的情况,那就是虚拟线程在两种情况下可能会被PINNED。
啥叫PINNED呢?
"PINNED"(绑定)在虚拟线程的上下文中,指的是虚拟线程被绑定到一个特定的底层操作系统线程上,期间它不能被调度器移动到其他载体线程上执行。
虽然PINNED不会使应用程序运行不正确,但是他还是会带来一定的影响。
虚拟线程的主要优势之一是它们能够在执行阻塞操作时被挂起,从而释放底层的操作系统线程给其他任务使用。这种机制极大地提升了并发处理的能力和系统资源的利用率。当虚拟线程被PINNED时,它就会一直占用一个底层线程,即使执行阻塞操作也无法被挂起,从而限制了其他虚拟线程的执行和系统的整体可伸缩性。
而且,虚拟线程设计的一个核心目标是提高系统执行大量并发任务的能力,特别是I/O密集型任务。固定虚拟线程到底层线程意味着减少了虚拟机能够灵活调度和优化任务执行的能力,因此降低了资源利用效率。
所以,我们需要尽可能的避免PINNED的发生。
而根据JEP425的描述,执行synchronized块或方法时,会发生PINNED,synchronized关键字涉及获取和释放内部监视器锁,因为这种锁机制依赖于操作系统级别的同步原语,执行synchronized块或方法的虚拟线程需要固定到一个底层线程上,以保证锁的正确管理,直到完成。
另一个场景就是调用本地方法或外部函数的时候,这些操作通常涉及与操作系统或外部资源的直接交互,必须在特定的操作系统线程上执行。
所以,不建议在使用虚拟线程的时候使用synchronized,推荐使用java.util.concurrent包中提供的更高级的同步机制,如ReentrantLock、Semaphore等。这些机制提供了更细粒度的控制和更高的灵活性,使得开发者能够构建出既安全又高效的并发应用。