变量的不同步 read/write 可能会导致数据竞争?
unsynchronized read/write of variables may cause data race?
在 Jack Shirazi 的 Java Performance Tuning 中写道:
This means that access and update of variables are automatically synchronized (as long as they are not longs or doubles). If a method consists solely of a variable access or assignment, there is no need to make it synchronized for thread safety, and every reason not to do so for performance. Thread safety extends further to any set of statements that are accessing or assigning to a variable independently of any other variable values.
根据上面的描述,像flag = true
这样的操作总是atomic,不需要synchronize
.
然而,another article将以下情况视为数据竞争:
class DataRaceExample {
static boolean flag = false;//w0
static void raiseFlag() {
flag = true;//w1
}
public static void main(String... args) {
ForkJoinPool.commonPool().execute(DataRaceExample::raiseFlag);
while (!flag);//r_i, where i ∈ [1, k), k may be infinite
System.out.print(flag);//r
}
}
作者说:
Now, all executions have data races, because the flag is not volatile
两篇文章的冲突让我很困惑。
在您的第二个示例中,循环将永远不会 运行 或 运行。
这是因为变量标志在第一次检查时只被读取一次。
如果flag是易变的,那么每次都从内存中读取。这允许另一个线程更改标志的值并且循环将看到它。
Jack Shirazi 错了。
像int
这样的原始变量的访问和更新是原子,而不是同步。
因为它是原子的,所以可以通过使其成为完全线程安全的 volatile
。否则,不同核心上的其他线程 运行 可能会看到 stale 值,因为 CPU 缓存 还没有已刷新。
Jack Shirazi 试图提出的观点是,根据 JMM,保证以原子方式执行对 double
和 long
以外的基本类型的非易失性访问。因此,同步是不必要的,例如在并发访问的情况下防止读写撕裂。
之所以会产生混淆,是因为他的书早于 JSR-133,并且他使用了 "automatically synchronized" 这样的术语,这不符合 JMM 中的现代同步概念。
在 Jack Shirazi 的 Java Performance Tuning 中写道:
This means that access and update of variables are automatically synchronized (as long as they are not longs or doubles). If a method consists solely of a variable access or assignment, there is no need to make it synchronized for thread safety, and every reason not to do so for performance. Thread safety extends further to any set of statements that are accessing or assigning to a variable independently of any other variable values.
根据上面的描述,像flag = true
这样的操作总是atomic,不需要synchronize
.
然而,another article将以下情况视为数据竞争:
class DataRaceExample {
static boolean flag = false;//w0
static void raiseFlag() {
flag = true;//w1
}
public static void main(String... args) {
ForkJoinPool.commonPool().execute(DataRaceExample::raiseFlag);
while (!flag);//r_i, where i ∈ [1, k), k may be infinite
System.out.print(flag);//r
}
}
作者说:
Now, all executions have data races, because the flag is not volatile
两篇文章的冲突让我很困惑。
在您的第二个示例中,循环将永远不会 运行 或 运行。
这是因为变量标志在第一次检查时只被读取一次。
如果flag是易变的,那么每次都从内存中读取。这允许另一个线程更改标志的值并且循环将看到它。
Jack Shirazi 错了。
像int
这样的原始变量的访问和更新是原子,而不是同步。
因为它是原子的,所以可以通过使其成为完全线程安全的 volatile
。否则,不同核心上的其他线程 运行 可能会看到 stale 值,因为 CPU 缓存 还没有已刷新。
Jack Shirazi 试图提出的观点是,根据 JMM,保证以原子方式执行对 double
和 long
以外的基本类型的非易失性访问。因此,同步是不必要的,例如在并发访问的情况下防止读写撕裂。
之所以会产生混淆,是因为他的书早于 JSR-133,并且他使用了 "automatically synchronized" 这样的术语,这不符合 JMM 中的现代同步概念。