挥发性与非挥发性
volatile vs not volatile
让我们考虑 Java
中的以下代码
int x = 0;
int who = 1
Thread #1:
(1) x++;
(2) who = 2;
Thread #2
while(who == 1);
x++;
print x; ( the value should be equal to 2 but, perhaps, it is not* )
(我不知道 Java 内存模型-假设它是强内存模型-我的意思是:(1) 和 (2) 不会交换)
Java 内存模型保证 access/store 到 32 位变量是原子的,因此我们的程序是安全的。但是,尽管如此我们还是应该使用属性 volatile
因为 *. x
的值可能等于1
,因为x
可以在Thread#2
读取时保存在寄存器中。要解决它,我们应该使 x
变量 volatile
。清楚了。
但是,那种情况呢:
int x = 0;
mutex m; ( just any mutex)
Thread #1:
mutex.lock()
x++;
mutex.unlock()
Thread #2
mutex.lock()
x++;
print x; // the value is always 2, why**?
mutex.unlock()
x
的值始终是 2
,尽管我们没有做到 volatile
。我是否正确理解 locking/unlocking 互斥锁与插入内存屏障有关?
让我们回顾一些事情。下面的说法是正确的: Java 内存模型保证 access/store 到 32 位变量是原子的。但是,这并不意味着您列出的第一个伪程序是安全的。仅仅因为两个语句在句法上是有序的 而不是 就意味着它们的更新的可见性也是按照其他线程查看的顺序排列的。线程 #2 可能会在 x 的增量可见之前看到由 who=2 引起的更新。使 x 易变仍然不会使程序正确。相反,使变量 'who' 易变会使程序正确。这是因为 volatile 以特定方式与 java 内存模型交互。
我觉得在对 volatile 的常识性理解的核心存在一些 'writing back to main memory' 概念,这是不正确的。 Volatile 不会将 Java 中的值写回主存。读取和写入 volatile 变量的作用是创建所谓的 happens-before 关系。当线程 #1 写入一个 volatile 变量时,您正在创建一种关系,以确保查看该 volatile 变量的任何其他线程 #2 也将能够 'view' 线程 #1 在此之前采取的所有操作。在您的示例中,这意味着使 'who' 不稳定。通过将值 2 写入 'who',您正在创建一个 happens-before 关系,以便当线程 #2 查看 who=2 时,它同样会看到 x 的更新版本。
在你的第二个例子中(假设你也想拥有 'who' 变量)互斥锁解锁创建了一个 happens-before 关系,正如我在上面指定的那样。因为这意味着其他线程查看互斥锁的解锁(即它们能够自己锁定它)它们将看到 x 的更新版本。
我会尽力解决这个问题。 Java 内存模型有点复杂,很难包含在单个 Whosebug post 中。请参阅 Brian Goetz 的 Java 并发实践 了解完整故事。
The value of x is always 2 though we don't make it volatile. Do I correctly understand that locking/unlocking mutex is connected with inserting memory barriers?
首先如果你想了解Java内存模型,你总是Chapter 17 of the spec要通读。
该规范说:
An unlock on a monitor happens-before every subsequent lock on that monitor.
是的,在您的显示器解锁时有一个内存可见性事件。 (我假设 "mutex" 你的意思是监视器。java.utils.concurrent
包中的大多数锁和其他 类 也有 happens-before 语义,检查文档。)
Happens-before 是 Java 的意思,它不仅保证事件有序,而且保证内存可见性。
We say that a read r of a variable v is allowed to observe a write w
to v if, in the happens-before partial order of the execution trace:
r is not ordered before w (i.e., it is not the case that
hb(r, w)), and
there is no intervening write w' to v (i.e. no write w' to v such
that hb(w, w') and hb(w', r)).
Informally, a read r is allowed to see the result of a write w if there
is no happens-before ordering to prevent that read.
全部来自17.4.5。通读起来有点混乱,但是如果您通读它,信息就在那里。
让我们考虑 Java
中的以下代码int x = 0;
int who = 1
Thread #1:
(1) x++;
(2) who = 2;
Thread #2
while(who == 1);
x++;
print x; ( the value should be equal to 2 but, perhaps, it is not* )
(我不知道 Java 内存模型-假设它是强内存模型-我的意思是:(1) 和 (2) 不会交换)
Java 内存模型保证 access/store 到 32 位变量是原子的,因此我们的程序是安全的。但是,尽管如此我们还是应该使用属性 volatile
因为 *. x
的值可能等于1
,因为x
可以在Thread#2
读取时保存在寄存器中。要解决它,我们应该使 x
变量 volatile
。清楚了。
但是,那种情况呢:
int x = 0;
mutex m; ( just any mutex)
Thread #1:
mutex.lock()
x++;
mutex.unlock()
Thread #2
mutex.lock()
x++;
print x; // the value is always 2, why**?
mutex.unlock()
x
的值始终是 2
,尽管我们没有做到 volatile
。我是否正确理解 locking/unlocking 互斥锁与插入内存屏障有关?
让我们回顾一些事情。下面的说法是正确的: Java 内存模型保证 access/store 到 32 位变量是原子的。但是,这并不意味着您列出的第一个伪程序是安全的。仅仅因为两个语句在句法上是有序的 而不是 就意味着它们的更新的可见性也是按照其他线程查看的顺序排列的。线程 #2 可能会在 x 的增量可见之前看到由 who=2 引起的更新。使 x 易变仍然不会使程序正确。相反,使变量 'who' 易变会使程序正确。这是因为 volatile 以特定方式与 java 内存模型交互。
我觉得在对 volatile 的常识性理解的核心存在一些 'writing back to main memory' 概念,这是不正确的。 Volatile 不会将 Java 中的值写回主存。读取和写入 volatile 变量的作用是创建所谓的 happens-before 关系。当线程 #1 写入一个 volatile 变量时,您正在创建一种关系,以确保查看该 volatile 变量的任何其他线程 #2 也将能够 'view' 线程 #1 在此之前采取的所有操作。在您的示例中,这意味着使 'who' 不稳定。通过将值 2 写入 'who',您正在创建一个 happens-before 关系,以便当线程 #2 查看 who=2 时,它同样会看到 x 的更新版本。
在你的第二个例子中(假设你也想拥有 'who' 变量)互斥锁解锁创建了一个 happens-before 关系,正如我在上面指定的那样。因为这意味着其他线程查看互斥锁的解锁(即它们能够自己锁定它)它们将看到 x 的更新版本。
我会尽力解决这个问题。 Java 内存模型有点复杂,很难包含在单个 Whosebug post 中。请参阅 Brian Goetz 的 Java 并发实践 了解完整故事。
The value of x is always 2 though we don't make it volatile. Do I correctly understand that locking/unlocking mutex is connected with inserting memory barriers?
首先如果你想了解Java内存模型,你总是Chapter 17 of the spec要通读。
该规范说:
An unlock on a monitor happens-before every subsequent lock on that monitor.
是的,在您的显示器解锁时有一个内存可见性事件。 (我假设 "mutex" 你的意思是监视器。java.utils.concurrent
包中的大多数锁和其他 类 也有 happens-before 语义,检查文档。)
Happens-before 是 Java 的意思,它不仅保证事件有序,而且保证内存可见性。
We say that a read r of a variable v is allowed to observe a write w
to v if, in the happens-before partial order of the execution trace:
r is not ordered before w (i.e., it is not the case that
hb(r, w)), and
there is no intervening write w' to v (i.e. no write w' to v such
that hb(w, w') and hb(w', r)).
Informally, a read r is allowed to see the result of a write w if there
is no happens-before ordering to prevent that read.
全部来自17.4.5。通读起来有点混乱,但是如果您通读它,信息就在那里。