虚拟线程的死锁代码

这个问题很有趣:这个程序使用 Java平台线程完成,但使用虚拟线程时却死锁。原因是虚拟线程被钉在同步块中(即不释放其载体),因此没有载体可用。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;

public class Main {
    private static final ReentrantLock lockA = new ReentrantLock();
    private static final ReentrantLock lockB = new ReentrantLock();

    private static final int CPUs = Runtime.getRuntime().availableProcessors();
    private static final CountDownLatch latch = new CountDownLatch(CPUs);

    public static void main(String[] args) throws Exception {
        lockA.lock();

        // VT 1
        Thread.startVirtualThread(() -> {
                lockB.lock();
                lockA.lock();
                lockA.unlock();
                lockB.unlock();
        });
        Thread.sleep(1000);

        System.out.println(
"Starting pinned virtual threads");

       
// VT 2..CPUs+1
        for (int i = 0; i < CPUs; i++) {
            Thread.startVirtualThread(() -> {
                synchronized (Main.class) {
                    lockB.lock();
                    lockB.unlock();
                    latch.countDown();
                    System.out.println(
"Exiting synchronized block");
                }
            });
        }

       
// This should unblock VT1, which unblocks VT2..CPUs+1
       
// Instead we deadlock, since VT1 can't execute as there are no
       
// available platform threads.
        lockA.unlock();
        latch.await();
    }
}

这一点并不广为人知,但你永远都不应该使用同步。同步

synchronized
关键字是一把锤子,人们对它的理解很差,更糟糕的是,它在 JVM 中的实现也很差。内核中存在大量替代方案是有原因的。

JEP 425 中明确提到了这一点。