理解发生在之前和同步

我试图理解Java 发生 – 在订单概念之前 ,有一些事情看起来很混乱。 据我所知,之前发生的只是行动集上的一个订单,并没有提供有关实时执行订单的任何保证。 实际上(强调我的):

应该注意的是,两个动作之间存在的先发生关系并不一定意味着它们必须在实现中以该顺序发生。 如果重新排序产生的 结果与合法执行一致,则不是非法的。

所以,它说的是,如果有两个动作w (写入)和r (读取)使得hb(w,r) ,而不是r 可能实际上在执行之前发生,但是不能保证它会。 读取r也观察到写入w

我如何确定在运行时随后执行两个操作? 例如:

 public volatile int v; public int c; 

操作:

 Thread A v = 3; //w Thread B c = v; //r 

这里我们有hb(w, r)但这并不意味着c在赋值后将包含值3 。 如何强制执行c分配3? 同步订单是否提供此类保证?

当JLS说线程A中的某个事件X在线程B中与事件Y的关系之前建立了一个事件时,它并不意味着X将在Y之前发生。

这意味着IF X发生在Y之前,那么两个线程都会同意 X发生在Y之前。也就是说,两个线程都会看到程序的内存处于与Y之前发生的X一致的状态。


这都是关于记忆的。 线程通过共享内存进行通信,但是当系统中有多个CPU时,所有CPU都试图访问同一个内存系统,那么内存系统就成了瓶颈。 因此,允许典型的多CPU计算机中的CPU延迟,重新排序和高速缓存存储器操作以加快速度。

当线程没有彼此交互时,这很有效,但是当它们实际上想要交互时它会导致问题:如果线程A将值存储到普通变量中,Java不能保证线程B何时(或者甚至是否 )会看到价值变化。

为了在重要时克服这个问题,Java为您提供了同步线程的某些方法。 也就是说,让线程同意程序内存的状态。 volatile关键字和synchronized关键字是在线程之间建立同步的两种方法。


我认为他们称之为“之前发生”的原因是为了强调关系的传递性:如果你能certificateA发生在B之前,你可以certificateB发生在C之前,那么根据JLS中规定的规则,你已经certificateA发生在C之前。

我想将上述语句与一些示例代码流相关联。

要理解这一点,让我们在下面有两个字段counterisActive

 class StateHolder { private int counter = 100; private boolean isActive = false; public synchronized void resetCounter() { counter = 0; isActive = true; } public synchronized void printStateWithLock() { System.out.println("Counter : " + counter); System.out.println("IsActive : " + isActive); } public void printStateWithNoLock() { System.out.println("Counter : " + counter); System.out.println("IsActive : " + isActive); } } 

并假设有三个线程T1,T2,T3在StateHolder的同一对象上调用以下方法:

T1调用resetCounter()和T2同时调用printStateWithLock() ,T1获取锁定
T3 – >在T1完成执行后调用printStateWithNoLock()

应该注意的是,两个动作之间存在的先发生关系并不一定意味着它们必须在实现中以该顺序发生。 如果重新排序产生的结果与合法执行一致,则不是非法的。

并且直线说,

根据上述声明,它为JVM,OS或底层硬件提供了灵活性,可以在resetCounter()方法中重新排序语句。 当T1执行时,它可以按以下顺序执行语句。

  public synchronized void resetCounter() { isActive = true; counter = 0; } 

这与声明一致并不一定意味着必须在实施中按顺序进行。

现在从T2的角度来看,这种重新排序没有任何负面影响,因为T1和T2都在同一个对象上同步,并且T2保证看到两个字段的变化都发生变化,无论重新排序是否有发生与否,因为之前发生了关系。 所以输出总是:

 Counter : 0 IsActive : true 

这是按照声明, 如果重新排序产生的结果与合法执行一致,则不是非法的

但从T3的角度来看,通过重新排序,T3可能会将isActive的更新值视为’true, but still see the计数器value as 100`,尽管T1已完成执行。

 Counter : 100 IsActive : true 

上述链接中的下一点进一步阐明了该声明并说:

更具体地说,如果两个动作共享发生在之前的关系,则它们不一定必须按照该顺序发生在它们不与之共享的任何代码之间。 例如,在具有另一个线程中的读取的数据争用中的一个线程中的写入可能看起来不按顺序发生到那些读取。

在这个例子中,T3遇到了这个问题,因为它没有与T1或T2发生过任何关系。 这是内联的不一定必须看起来按顺序发生在他们不共享事先关系的任何代码上。

注意:为了简化这种情况,我们有单线程T1修改状态,T2和T3读取状态。 有可能

T1稍后将counter to 0更新counter to 0
T2将isActive修改为true并且在某段时间后看到counter is 0
打印状态的T3仍然可以看到only isActive as true but counter is 100 ,尽管T1和T2都已完成执行。

至于最后一个问题:

我们有hb(w,r),但这并不意味着c在赋值后将包含值3。 如何强制执行c分配3?

 public volatile int v; public int c; Thread A v = 3; //w Thread B c = v; //r 

因为v是易失性的,所以按照Happens-before Order

写入易失性字段(第8.3.1.4节) – 在每次后续读取该字段之前发生。

因此可以安全地假设当线程B尝试读取变量v ,它将始终读取更新的值,并且c将在上面的代码中分配3。

解读@James的回答:

 // Definition: Some variables private int first = 1; private int second = 2; private int third = 3; private volatile boolean hasValue = false; // Thread A first = 5; second = 6; third = 7; hasValue = true; // Thread B System.out.println("Flag is set to : " + hasValue); System.out.println("First: " + first); // will print 5 System.out.println("Second: " + second); // will print 6 System.out.println("Third: " + third); // will print 7 

如果你想要一个线程的变量写入语句时看到的内存(内存和CPU缓存)的状态/值,

线程A中hasValue=true (写入语句)看到的内存状态:

first具有值5, second具有值6, third具有值7

从后面的每一个中可以看出(为什么后续即使在这个例子中只有一个读取线程B?我们很multithreadingC与线程B完全相似)由另一个线程读取同一个变量的语句,然后标记该变量volatile

如果线程A中的X( hasValue=true发生在线程B中的Y( sysout(hasValue)之前 ,则行为应该好像X发生在同一线程中的Y之前(在X处看到的内存值应该从Y开始是相同的)

这里我们有hb(w,r),但这并不意味着c在赋值后将包含值3。 如何强制执行c分配3? 同步订单是否提供此类保证?

你的榜样

 public volatile int v; public int c; Actions: Thread A v = 3; //w Thread B c = v; //r 

在您的示例中,您不需要volatile for v 。 我们来看一个类似的例子

 int v = 0; int c = 0; volatile boolean assigned = false; 

操作:

线程A.

 v = 3; assigned = true; 

线程B

 while(!assigned); c = v; 
  1. assigned字段是易变的。
  2. 只有在assignedtrue后我们才会在Thread Bc = v语句( while(!assigned)负责)。
  3. 如果我们有volatile – 我们happens before就已经happens before
  4. happens before意味着,如果我们看到assigned == true – 我们将看到在声明assigned = true之前发生的所有事情:我们将看到v = 3
  5. 所以当我们assigned == true – >时,我们有v = 3
  6. 结果我们有c = 3

什么会发生没有volatile

 int v = 0; int c = 0; boolean assigned = false; 

操作:

线程A.

 v = 3; assigned = true; 

线程B

 while(!assigned); c = v; 

我们现在已经assigned没有volatile的。

在这种情况下, Thread Bc值可以等于0或3。 所以c == 3没有任何保证。