Java - Lock如何保证发生之前关系?

Bob*_*r02 4 java concurrency multithreading locking volatile

让我们考虑一下 Java 中的以下标准同步:

public class Job {
   private Lock lock = new ReentrantLock();

   public void work() {
       lock.lock();
       try {
           doLotsOfWork();
       } finally {
           lock.unlock();
       }
   }
}
Run Code Online (Sandbox Code Playgroud)

根据Javadoc,我理解这相当于synchronized块。我正在努力了解这实际上是如何在较低级别上执行的。

Lock有一个易失性的状态,在调用lock()它时会执行易失性读取,然后在释放时执行易失性写入。对一个对象的状态进行写入如何确保可能涉及许多不同对象的指令doLotsOfWork不会乱序执行?

或者想象一下 doLotsOfWork 实际上被 1000 多行代码取代。显然,编译器无法提前知道锁内某处存在 volatile,因此它需要停止对指令重新排序。lock/unlock那么,即使它是围绕单独对象的易失性状态构建的,如何保证发生之前呢?

Eug*_*ene 5

好吧,如果我理解正确的话,那么你的答案就在这里。易失性写入和读取会引入内存屏障LoadLoadLoadStore等,禁止重新排序。在 CPU 级别,这被转换为实际的内存屏障,例如mfencelfence(CPU 也通过其他一些机制强制不重新排序,因此您可能还会在机器代码中看到其他内容)。

这是一个小例子:

i = 42;
j = 53;
[StoreStore]
[LoadStore]
x = 1; // volatile store
Run Code Online (Sandbox Code Playgroud)

ij分配可以在 then 之间重新排序,但它们不能x=1x 或换句话说i and j不能低于 x。

同样适用于volatile reads.

对于您的示例,内部的每个操作都doLotsOfWork可以根据编译器的意愿重新排序,但不能使用lock operations.

另外,当您说编译器无法知道存在 a 时volatile read/write,您就有点错误了。它必须知道这一点,否则就没有其他方法来阻止这些重新排序。

另外,最后一点:从 jdk-8 开始,您可以通过Unsafe提供非易失性之外的方法来强制执行非重新排序。