首页-玩家时代2娱乐-玩家时代2平台【注册登陆】
首页-玩家时代2娱乐-玩家时代2平台【注册登陆】报道,Java 19 中的虚拟线程由 JDK 负责调度。虚拟线程创建之后,会处于不同的状态之中。这些状态包括:
NEW:刚被创建之后的初始状态
STARTED:线程已启动
RUNNING:线程运行中
PARKING:线程尝试进入停驻状态
PARKED:线程处于停驻状态
PINNED:线程处于锁定状态
RUNNABLE:线程处于可运行状态
YIELDING:线程尝试让出运行权
TERMINATED:线程已终止
这些状态定义在 java.lang.VirtualThread 中。
private static final int NEW= 0;private static final int STARTED= 1;private static final int RUNNABLE = 2; // runnable-unmountedprivate static final int RUNNING= 3; // runnable-mountedprivate static final int PARKING= 4;private static final int PARKED = 5; // unmountedprivate static final int PINNED = 6; // mountedprivate static final int YIELDING = 7; // Thread.yieldprivate static final int TERMINATED = 99;// final state
这些状态的变化如下图所示
虚拟线程的状态变化
在这些状态中,比较特殊的是 PARKING、PARKED、PINNED、YIELDING 等状态。这些是虚拟线程独有的。我们知道,虚拟线程与平台线程是 M 对 N 的关系。虚拟线程需要绑定到平台线程上之后才能运行。在运行过程中,如果由于某种原因无法继续执行,可以调用 VirtualThread 的 park 方法来尝试停驻,让出运行权。如果让出成功,该虚拟线程会从平台线程上解除绑定,转为停驻状态;如果让出失败,该虚拟线程会被锁定在平台线程上,导致其他虚拟线程无法使用该平台线程。
另外一种方式是应用代码主动调用 Thread.yield 来让出运行权。如果让出成功,虚拟线程会处于 RUNNABLE 状态,等待下次调度;如果让出失败,虚拟线程仍然继续运行。
Java 19 中
java.util.concurrent.locks.LockSupport 的 park 方法,会调用虚拟线程的 park 方法。这就意味着当代码因为多线程同步的原因,而无法运行时,虚拟线程会尝试停驻。
public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker);try {if (t.isVirtual()) { VirtualThreads.park(); } else { U.park(false, 0L); } } finally { setBlocker(t, null); } }
当虚拟线程处于锁定状态时,平台线程被该虚拟线程完全占用,会减少 JDK 调度器中可用的平台线程数量。作为调度的 ForkJoinPool 会创建新的平台线程来作为补偿,但是数量不会超过设置的最大值。
为了提高系统的吞吐量,应该尽可能地避免虚拟线程的锁定。在执行 synchronized 方法或块时,以及执行 native 方法或外部方法时,虚拟线程必定处于锁定状态。这是因为这两种方法由 JVM 来处理,JDK 的调度器无法影响这两种方法;而 Java 多线程类库中的类就没有这个问题。它们使用的是 LockSupport 中的 park 方法。当需要等待时,虚拟线程会尝试停驻而从平台线程上解除绑定,这就释放了平台线程,允许其他虚拟线程来使用,有助于提升系统的吞吐量。从这个角度来说,把 synchronized 替换成 Java 类库中的锁,可能会提升系统的吞吐量。
有几种方式可以调试虚拟线程的锁定。首先是通过系统属性 jdk.tracePinnedThreads,把该属性的值设置为 full 或者空字符串,可以在虚拟线程锁定时,输出详细的线程堆栈信息;如果该属性的值是 short,则仅输出虚拟线程的信息。
另外一种做法是使用 JFR,并监听 VirtualThreadPinnedEvent 事件,从而分析出现线程锁定的情况。