vitual thread线程模型
可以看到 virtual thread 是通过 JVM 调度的,而平台线程或者 carrier 线程(java thread)是与操作系统的线程一一对应的,由操作系统调度。virtual thread与 platform/carrier thread是多对一的关系。
vittual thread执行时,需要依附于 platform thread。
ThreadLocal
ThreadLocal 有两个作用:
- 在线程上下文中传递数据(该使用场景在虚拟线程中也需要,但是建议使用ScopedValue)
- 对于线程不安全的对象,如果在每个线程中都创建会比较"重",因此可以通过 ThreadLocal 缓存。 例如:
static final ThreadLocal<SimpleDateFormat> cachedFormatter =
ThreadLocal.withInitial(SimpleDateFormat::new);
void foo() {
...
cachedFormatter.get().format(...);
...
}
建议改为:DateTimeFormatter is immutable, and so a single instance can be shared by all threads:
static final DateTimeFormatter formatter = DateTimeFormatter….;
void foo() {
...
formatter.format(...);
...
}
对于第二个场景,虚拟线程不适用。因为虚拟线程会很多(每个 task 都会有一个虚拟线程) 而且虚拟线程不需要也不会被复用,因此每次都会实例化新的SimpleDateFormat,这违背了 ThreadLocal 的初衷。
pinned 及其影响
在虚拟线程中,如果代码中有 synchronized 关键字,而该代码快或者方法执行比较慢,可能会被 pinned。这是因为 synchronized 是基于CAS 实现的。当 virtual thread执行时,virtual thread 与 platform thread建立绑定关系,在 synchronized 自旋时,JVM 无法将该 virtual thread 调度走。而 ReentrantLock是基于 AQS 实现的,在virtual thread 场景下,不会表现的很差。
The key difference is that
ReentrantLock, AQS, directUnsafe/VarHandleand similar synchronization primitives use non-blocking CAS technique whilesynchronizedblocks the thread, at least in a case of contention.
To understand the difference between the two, I'd advise to learn more about CAS and/or experiment with Java
Atomic*methodscompareAndSet.
As a side note,
ReentrantLock, AQS and few other other synchronization primitives still may block the thread (in fact, it is necessary, generally speaking), it is done by callingLockSupport.parkmethods. Under the hood,LockSupport.parkalso invokes a native method, but for some reason this is not (or lesser?) problem for Virtual Threads.
调试 virtual thread
JFR
在java 进程启动的时候增加如下配置参数:
-XX:StartFlightRecording:filename=recording.jfr,dumponexit=true,settings=default.jfc
通过 jfr 命令可以解析进程生成的recording.jfr文件。
jfr print --events jdk.VirtualThreadStart,jdk.VirtualThreadEnd,jdk.VirtualThreadPinned,jdk.VirtualThreadSubmitFailed recording.jfr
其中vscode 增加配置的方式为: 运行 -> 添加配置
jcmd
jcmd <PID> Thread.dump_to_file -format=json <file>