如何理解发生 - 在一致之前

new*_*man 11 java memory-model

JLS的第17章中,它引入了一个概念:发生在一致之前.

如果对于A中的所有读取r,其中W(r)是r看到的写入动作,那么一组动作A发生 - 在一致之前,不是hb(r,W(r))或那里的情况在A中存在写w,使得wv = rv和hb(W(r),w)和hb(w,r)"

在我的理解中,它等于以下几个词:...,既不是......也不是......

所以我的前两个问题是:

  • 我的理解对吗?
  • "wv = rv"是什么意思?

它还给出了一个例子:17.4.5-1

Thread 1 Thread 2

B = 1; A = 2; 

r2 = A; r1 = B; 
Run Code Online (Sandbox Code Playgroud)

在第一个执行顺序中:

1: B = 1;

3: A = 2;

2: r2 = A;  // sees initial write of 0

4: r1 = B;  // sees initial write of 0
Run Code Online (Sandbox Code Playgroud)

订单本身已经告诉我们两个线程交替执行,所以我的第三个问题是:左数是什么意思?

在我的理解中,r2和r1的原因可以看出0的初始写入是A和B都不是volatile字段.所以我的第四个问题是:我的理解是否正确?

在第二个执行顺序中:

1: r2 = A;  // sees write of A = 2

3: r1 = B;  // sees write of B = 1

2: B = 1;

4: A = 2;
Run Code Online (Sandbox Code Playgroud)

根据事先一致性的定义,不难理解这种执行顺序是否发生 - 在一致之前(如果我的第一个理解是正确的).所以我的第五个和第六个问题是:在现实世界中是否存在这种情况(读取后面会发生的写入)?如果确实如此,你能给我一个真实的例子吗?

Pet*_*rey 13

每个线程可以在具有自己的缓存的不同核心上.这意味着一个线程可以写入存储在寄存器或其本地缓存中的值,并且该值在一段时间内对另一个线程不可见.(毫秒并不少见)

一个更极端的例子是读取线程的代码被优化,假设因为它永远不会改变值,所以它不需要从内存中读取它.在这种情况下,优化代码永远不会看到另一个线程执行的更改.

在这两种情况下,使用volatile确保读取和写入以一致的顺序发生,并且两个线程都看到相同的值.这有时被描述为始终从主存储器读取,但并非必须如此,因为缓存可以直接相互通信.(因此,性能影响远小于您的预期)


yoA*_*ex5 8

发生在之前

我们看一下并发理论中的定义:

原子性- 是操作的一个属性,可以作为单个事务完全执行并且不能部分执行。例如Atomic operations[例子]

可见性- 如果一个线程进行了更改,则其他线程可以看到它们。volatile在 Java 5 之前happens-before

排序- 编译器能够更改源代码的操作/指令的顺序以进行一些优化。

例如happens-before这是一种memory barrier有助于解决的Visibility问题Ordering。发生之前的好例子是volatile[About]synchronized监视[About]

一个很好的例子atomicityCompare and swap( ) 模式的( )CAS实现,它应该是原子的,并且允许在多线程环境中更改变量。如果满足以下条件,您可以编写自己的实现:check then actCTACTA

  • volatile + synchronized
  • java.util.concurrent.atomicwith sun.misc.Unsafe(内存分配、无需构造函数调用即可实例化...)从中Java 5使用JNI和 CPU 优势。

CAS算法有三个参数(A(地址),O(旧值),N(新值))。

If value by A(address) == O(old value) then put N(new value) into A(address), 
else O(old value) = value from A(address) and repeat this actions again
Run Code Online (Sandbox Code Playgroud)

发生在之前

官方文档

两个操作可以先行发生关系排序。如果一个操作发生在另一个操作之前,则第一个操作对第二个操作可见并且排序在第二个操作之前。

在此输入图像描述

以挥发性[关于]为例

易失性字段的写入发生该字段的每次后续读取之前。

让我们看一下例子:

// Definitions
int a = 1;
int b = 2;
volatile boolean myVolatile = false;

// Thread A. Program order
{
    a = 5;
    b = 6;
    myVolatile = true; // <-- write
}

//Thread B. Program order
{
    //Thread.sleep(1000); //just to show that writing into `myVolatile`(Thread A) was executed before

    System.out.println(myVolatile); // <-- read
    System.out.println(a);  //prints 5, not 1
    System.out.println(b);  //prints 6, not 2
}
Run Code Online (Sandbox Code Playgroud)

可见性- 当Thread A 更改/写入易失性变量时,它还会将所有先前的更改推送到RAM - 主内存中,因此所有非易失性变量都将是最新的并且对其他线程可见

订购

  • 所有写入 volatile 变量之前的Thread A操作都会被调用。JVM 能够对它们重新排序,但保证在写入 volatile 变量之前Thread A不会执行任何操作。

  • 读取 volatile 变量后的Thread B所有操作都会在之后调用。JVM 能够对它们重新排序,但保证在读取 volatile 变量后Thread B不会在其之前调用任何操作。

[并发与并行]

  • 关于顺序,不仅仅是编译器改变顺序或感知顺序(指令可能按顺序执行,但其他内核/CPU 可能不会以相同的顺序看到它们对内存的影响)。CPU 的流水线架构可以并且确实也可以更改不依赖于彼此输出的指令的顺序。 (2认同)