JMM 在实践中
JMM In Practice
考虑以下用Java编写的多线程代码:
共享变量:
boolean n; // non-volatile
volatile boolean v; // volatile
线程 1:
v = true;
System.out.println("n=" + n);
线程 2:
n = true;
System.out.println("v=" + v);
最初假设 n = v = false
。
现在:
v=false
的输出是否意味着n=true
的输出?
- 如果
n
是不稳定的,会发生什么变化?
- 如果
n
是 java.util.List
会发生什么变化(因此 n = true
变为 n.add("something")
并且输出 n=true
转换为 ["something"]
)?
- UPD3: 如果
v
是 AtomicBoolean
并且对它的所有读取和写入都使用 compareAndSet
语义执行怎么办?
您能根据 Java 记忆模型论证您的立场吗?
更新: 请将 System.out.println("n=" + n)
视为对 n
的阅读。 v
.
也一样
UPD2: 您能否提供对第 1 和第 4 种情况的一些分析,如 JSR-133 秒所示。 8?
这里涉及两个因素。
- 您有一个
volatile
字段,但是
- 你有一个
synchronized
调用 System.out.println
这会起到 read/write 障碍
在两个线程中,您都在执行写入和读取。写入屏障并不能保证读取屏障。
Does the output of v=false imply the output of n=true?
如果您看到 v=false
,您可能会看到 n=false
,反之亦然
What would change if n were volatile?
不一定,您可能会看到行为发生变化,具体取决于您 运行 所在的体系结构,但是您仍然可以看到某些机器的不确定行为。
What would change if n were java.util.List
主要变化是您要替换写屏障,例如n = true
with a read barrier n.method
这意味着你不再有写屏障(调用同步方法除外)
这样做意味着线程之间的一致性失败的原因更多。
实际上代码可以执行三种方式:
// Thread 1 runs first
T1: v = true;
T1: System.out.println("n=" + n); // prints n=false
// Thread 2 runs second
T2: n = true;
T2: System.out.println("v=" + v); // prints v=true
// They run in parallel, so assignments first, in any order
T1: v = true;
T2: n = true;
// Print statements second, in any order
T1: System.out.println("n=" + n); // may print n=true or n=false (depends on CPU caching)
// will print n=true if n is volatile
T2: System.out.println("v=" + v); // prints v=true
// Thread 2 runs first
T2: n = true;
T2: System.out.println("v=" + v); // prints v=false
// Thread 1 runs second
T1: v = true;
T1: System.out.println("n=" + n); // may print n=true or n=false (depends on CPU caching)
// will print n=true if n is volatile
Does the output of v=false imply the output of n=true?
没有。正如您在第三种情况中看到的,n
可能会打印任一值。
What would change if n were volatile?
如评论所述,n
将在场景 2 和 3 中打印 true
,而不是不确定的。
What would change if n were java.util.List (so that n = true becomes n.add("something") and the output n=true transforms into ["something"])?
这假定 n
在场景开始之前已经是 List
,因此 n
的波动性无关紧要。
n
的打印是否会看到插入的值取决于列表的类型。如果不是并发列表,答案与问题 1 相同:它可能会或可能不会看到新值。非并发列表的例子:
- ArrayList
- LinkedList
- Arrays.asList(...) (如果调用
set()
,因为你不能在这种列表上调用 add()
)
如果列表是并发的,它将看到新值。并发列表示例:
- CopyOnWriteArrayList (并发)
- Vector and subclass Stack (同步)
- Collections.synchronizedList(...) (同步)
考虑以下用Java编写的多线程代码:
共享变量:
boolean n; // non-volatile
volatile boolean v; // volatile
线程 1:
v = true;
System.out.println("n=" + n);
线程 2:
n = true;
System.out.println("v=" + v);
最初假设 n = v = false
。
现在:
v=false
的输出是否意味着n=true
的输出?- 如果
n
是不稳定的,会发生什么变化? - 如果
n
是java.util.List
会发生什么变化(因此n = true
变为n.add("something")
并且输出n=true
转换为["something"]
)? - UPD3: 如果
v
是AtomicBoolean
并且对它的所有读取和写入都使用compareAndSet
语义执行怎么办?
您能根据 Java 记忆模型论证您的立场吗?
更新: 请将 System.out.println("n=" + n)
视为对 n
的阅读。 v
.
UPD2: 您能否提供对第 1 和第 4 种情况的一些分析,如 JSR-133 秒所示。 8?
这里涉及两个因素。
- 您有一个
volatile
字段,但是 - 你有一个
synchronized
调用System.out.println
这会起到 read/write 障碍
在两个线程中,您都在执行写入和读取。写入屏障并不能保证读取屏障。
Does the output of v=false imply the output of n=true?
如果您看到 v=false
,您可能会看到 n=false
,反之亦然
What would change if n were volatile?
不一定,您可能会看到行为发生变化,具体取决于您 运行 所在的体系结构,但是您仍然可以看到某些机器的不确定行为。
What would change if n were java.util.List
主要变化是您要替换写屏障,例如n = true
with a read barrier n.method
这意味着你不再有写屏障(调用同步方法除外)
这样做意味着线程之间的一致性失败的原因更多。
实际上代码可以执行三种方式:
// Thread 1 runs first
T1: v = true;
T1: System.out.println("n=" + n); // prints n=false
// Thread 2 runs second
T2: n = true;
T2: System.out.println("v=" + v); // prints v=true
// They run in parallel, so assignments first, in any order
T1: v = true;
T2: n = true;
// Print statements second, in any order
T1: System.out.println("n=" + n); // may print n=true or n=false (depends on CPU caching)
// will print n=true if n is volatile
T2: System.out.println("v=" + v); // prints v=true
// Thread 2 runs first
T2: n = true;
T2: System.out.println("v=" + v); // prints v=false
// Thread 1 runs second
T1: v = true;
T1: System.out.println("n=" + n); // may print n=true or n=false (depends on CPU caching)
// will print n=true if n is volatile
Does the output of v=false imply the output of n=true?
没有。正如您在第三种情况中看到的,n
可能会打印任一值。
What would change if n were volatile?
如评论所述,n
将在场景 2 和 3 中打印 true
,而不是不确定的。
What would change if n were java.util.List (so that n = true becomes n.add("something") and the output n=true transforms into ["something"])?
这假定 n
在场景开始之前已经是 List
,因此 n
的波动性无关紧要。
n
的打印是否会看到插入的值取决于列表的类型。如果不是并发列表,答案与问题 1 相同:它可能会或可能不会看到新值。非并发列表的例子:
- ArrayList
- LinkedList
- Arrays.asList(...) (如果调用
set()
,因为你不能在这种列表上调用add()
)
如果列表是并发的,它将看到新值。并发列表示例:
- CopyOnWriteArrayList (并发)
- Vector and subclass Stack (同步)
- Collections.synchronizedList(...) (同步)