Hon*_*gbo 30 java concurrency mutable volatile
在Java中,我理解volatile关键字提供变量的可见性.问题是,如果变量是对可变对象的引用,那么volatile还是为该对象内的成员提供了可见性吗?
在下面的示例中,如果多个线程正在访问volatile Mutable m并更改value?,它是否正常工作?
例
class Mutable {
private int value;
public int get()
{
return a;
}
public int set(int value)
{
this.value = value;
}
}
class Test {
public volatile Mutable m;
}
Run Code Online (Sandbox Code Playgroud)
jta*_*orn 17
这是对volatile的一些细节的侧面说明.在这里写这个是因为评论太多了.我想举一些例子来说明volatile如何影响可见性,以及jdk 1.5中的变化情况.
给出以下示例代码:
public class MyClass
{
private int _n;
private volatile int _volN;
public void setN(int i) {
_n = i;
}
public void setVolN(int i) {
_volN = i;
}
public int getN() {
return _n;
}
public int getVolN() {
return _volN;
}
public static void main() {
final MyClass mc = new MyClass();
Thread t1 = new Thread() {
public void run() {
mc.setN(5);
mc.setVolN(5);
}
};
Thread t2 = new Thread() {
public void run() {
int volN = mc.getVolN();
int n = mc.getN();
System.out.println("Read: " + volN + ", " + n);
}
};
t1.start();
t2.start();
}
}
Run Code Online (Sandbox Code Playgroud)
的该测试代码的行为在JDK1.5 +被很好地定义,但不明确定义的预JDK1.5.
在pre-jdk1.5世界中,易失性访问和非易失性访问之间没有确定的关系.因此,该计划的产出可能是:
在jdk1.5 +世界中,volatile的语义被改变,因此易失性访问以与同步完全相同的方式影响非易失性访问.因此,在jdk1.5 +世界中只有某些输出是可能的:
输出3.是不可能的,因为从volatile_volN读取"5"会在2个线程之间建立同步点,这意味着在分配给_volN之前从t1获取的所有操作必须对t2可见.
进一步阅读:
在您的示例中,volatile关键字仅保证由任何线程写入"m"的最后一个引用对随后读取"m"的任何线程都是可见的.
它不保证你的get().
所以使用以下顺序:
Thread-1: get() returns 2
Thread-2: set(3)
Thread-1: get()
Run Code Online (Sandbox Code Playgroud)
你回到2而不是3是完全合法的. volatile不会改变任何东西.
但如果你改变你的Mutable课程:
class Mutable {
private volatile int value;
public int get()
{
return a;
}
public int set(int value)
{
this.value = value;
}
}
Run Code Online (Sandbox Code Playgroud)
然后保证get()Thread-1 中的第二个返回3.
但请注意,volatile通常不是最佳同步方法.
在你简单的get/set示例中(我知道它只是一个例子)一个类AtomicInteger,使用适当的同步并实际提供有用的方法,会更好.
volatile仅提供有关对此声明的Object的引用的保证.该实例的成员不会同步.
根据维基百科,你有:
- (在所有版本的Java中)对volatile变量的读写都有一个全局排序.这意味着访问volatile字段的每个线程将在继续之前读取其当前值,而不是(可能)使用缓存值.(但是,无法保证常规读写的易失性读写的相对顺序,这意味着它通常不是一个有用的线程构造.)
- (在Java 5或更高版本中)易失性读取和写入建立了先发生关系,就像获取和释放互斥锁一样.
所以基本上你所拥有的是通过声明字段volatile,与它交互创建一个"同步点",之后任何更改都将在其他线程中可见.但在那之后,使用get()或set()未经启动.在Java规范有一个更透彻的解释.
| 归档时间: |
|
| 查看次数: |
10528 次 |
| 最近记录: |