JavaMemoryModel:aJLSstatementaboutsequentialconsistencyseemsincorrect

I'm reading Chapter 17. Threads and Locks of JLS and the following statement about sequential consistency in Java seems incorrect to me:

If a program has no data races, then all executions of the program will appear to be sequentially consistent.

They define a data race as:

When a program contains two conflicting accesses (§17.4.1) that are not ordered by a happens-before relationship, it is said to contain a data race.

They define conflicted accesses as:

Two accesses to (reads of or writes to) the same variable are said to be conflicting if at least one of the accesses is a write.

And finally they have following about happens-before relationship:

A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.

My problem with the 1st statement is that I think I can come up with a Java program which has no data races and allows sequentially inconsistent executions:

// Shared code
volatile int vv = 0;
int v1 = 0;
int v2 = 0;


// Thread1       Thread2
   v1 = 1;
   v2 = 2;
   vv = 10;      while(vv == 0) {;}
                 int r1 = v1;
                 int r2 = v2;
                 System.out.println("v1=" + r1 + " v2=" + r2);
   v1 = 3;
   v2 = 4;
   vv = 20;

In the code above I also showed with indentation how the threads' code is interleaved in runtime.

So, as I understand, this program:

  • has no data races: reads of v1 and v2 in Thread2 are synchronized-with writes in Thread1
  • can output v1=1 v2=4 (which violates sequential consistency).

As a result, the initial statement from JLS

If a program has no data races, then all executions of the program will appear to be sequentially consistent.

seems incorrect to me.

Am I missing something or did I make a mistake somewhere?

EDIT: user chrylis-cautiouslyoptimistic correctly pointed out that the code I gave can output v1=1 v2=4 with sequential consistency — the lines in threads' code simply should be interleaved a little bit differently.

So here is the slightly modified code (I've changed the order of reads) for which sequential consistency cannot output v1=1 v2=4, but everything still applies.

// Shared code
volatile int vv = 0;
int v1 = 0;
int v2 = 0;


// Thread1       Thread2
   v1 = 1;
   v2 = 2;
   vv = 10;      while(vv == 0) {;}
                 int r2 = v2;
                 int r1 = v1;
                 System.out.println("v1=" + r1 + " v2=" + r2);
   v1 = 3;
   v2 = 4;
   vv = 20;

回答

你的错误是在子弹点#1:的读取v1v2 同步,与

还有之前发生创建关系,只能通过与互动vv,因此,例如在这种情况下,如果您添加vv到您的打印语句的开始,您将得到保证不会看vv=20,v2=4。由于您忙于等待vv变为非零但随后不再与它交互,因此唯一的保证是您将看到在变为非零之前发生的所有效果(1 和 2 的赋值)。您可能还会看到未来的效果,因为您没有任何进一步的发生前

即使您将所有变量声明为 volatile,您仍然可以输出,v1=1,v2=4 因为变量的多线程访问没有定义的 order,并且全局序列可以是这样的:

  1. T1:写 v1=1
  2. T1:写 v2=2
  3. T1:写入vv=10(线程 2不能在此之前退出 while 循环,并且保证看到所有这些效果。)
  4. T2:阅读 vv=10
  5. T2:阅读 v1=1
  6. T1:写 v1=3
  7. T1:写 v2=4
  8. T2:阅读 v2=4

在这些步骤中的每一步之后,内存模型保证所有线程将看到 volatile 变量的相同值,但是您有数据竞争,这是因为访问不是原子的(分组的)。为了确保您在一个组中看到它们,您需要使用一些其他方法,例如在synchronized块中执行或将所有值放入一个记录类中并使用volatileAtomicReference换出整个记录。

正式地,JLS 定义的数据竞争包括操作 T1(写 v1=3)和 T2(读 v1)(以及 v2 上的第二个数据竞争)。这些冲突的访问(因为T1访问是写),但在这两个事件发生后T2(读VV),他们没有下令在彼此相关


以上是JavaMemoryModel:aJLSstatementaboutsequentialconsistencyseemsincorrect的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>