同步锁由最短等待线程获取

MK *_*ung 6 java multithreading jvm locking

我知道这synchronize(LOCK)是不公平的,这意味着不能保证等待时间最长的线程会赢得锁。然而,在我下面的小实验中,似乎锁是由最短的等待线程获取的......

public class Demo {
    public static final Object LOCK = new Object();

    public void unfairDemo(){

        // Occupy the lock for 2 sec
        new Thread(() -> {
            synchronized (LOCK) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        // Spawn 10 new threads, each with 100ms interval, to see which can win the lock
        // If lock is fair then it should print the i in asc order
        for (var i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                System.out.println("Added " + String.valueOf(finalI) + "th element to wait for lock");

                synchronized (LOCK) {
                    System.out.println("I got the lock, says " + String.valueOf(finalI) + "-th thread");
                }
            }).start();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try {
            // Keep the program alive
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

运行不公平演示()打印以下内容:

Added 0th element to wait for lock
Added 1th element to wait for lock
Added 2th element to wait for lock
Added 3th element to wait for lock
Added 4th element to wait for lock
Added 5th element to wait for lock
Added 6th element to wait for lock
Added 7th element to wait for lock
Added 8th element to wait for lock
Added 9th element to wait for lock
I got the lock, says 9-th thread
I got the lock, says 8-th thread
I got the lock, says 7-th thread
I got the lock, says 6-th thread
I got the lock, says 5-th thread
I got the lock, says 4-th thread
I got the lock, says 3-th thread
I got the lock, says 2-th thread
I got the lock, says 1-th thread
I got the lock, says 0-th thread
Run Code Online (Sandbox Code Playgroud)

我预计顺序会被打乱,但无论我如何尝试,结果都是相反的。我在这里做错了什么?

man*_*uti 5

有许多来源,例如this,已经表明不应假设线程获取锁的顺序。但这并不意味着必须打乱订单。

它可能至少取决于 JVM 实现。例如,这个关于 HotSpot 的文档说:

争用同步操作使用先进的自适应旋转技术来提高吞吐量,即使对于具有大量锁争用的应用程序也是如此。因此,同步性能变得如此之快,以至于对于绝大多数现实世界的程序来说,这不是一个重要的性能问题。

...

在没有争用的正常情况下,同步操作将完全在快速路径中完成。但是,如果我们需要阻塞或唤醒线程(分别在 monitorenter 或 monitorexit 中),快速路径代码将调用慢速路径。慢路径实现是在本机 C++ 代码中实现的,而快速路径是由 JIT 发出的。

我不是 HotSpot 的专家(也许其他人可以提供更权威的答案),但基于C++ 代码,看起来竞争线程将被推送到 LIFO 结构上,这可能解释了您的类似堆栈的顺序观察到的:

// * Contending threads "push" themselves onto the cxq with CAS
//   and then spin/park.
...
//   Cxq points to the set of Recently Arrived Threads attempting entry.
//   Because we push threads onto _cxq with CAS, the RATs must take the form of
//   a singly-linked LIFO.
Run Code Online (Sandbox Code Playgroud)

  • 你说得对。线索是,在给定的示例中,线程尝试按确定性顺序获取锁,并在它们之间有很长的延迟(“Thread.sleep(100)”)。因此,线程在快速路径和自旋阶段都无法获取锁,并通过慢速路径 - 停车。当线程使用 CAS 将自己添加到“Cxq”列表时,线程之间也不存在争用,并且正如您上面引用的那样,“Cxq”是 LIFO。综上所述,锁定算法中确实存在随机性(由于 CAS 和自旋循环),但在这个特定示例中 100 毫秒的延迟避免了随机性。 (4认同)