Dim*_*ime 11 java concurrency volatile
情况如下:
问题是:我是否必须使此类的所有变量都不稳定?
关注:
注意:我知道构建器模式,但由于其他几个原因我无法应用它:(
编辑: 由于我觉得Mathias和axtavt的两个答案不太匹配,我想补充一个例子:
假设我们有一个foo
班级:
class Foo {
public int x=0;
}
Run Code Online (Sandbox Code Playgroud)
并且两个线程正在使用它,如上所述:
// Thread 1 init the value:
Foo f = new Foo();
f.x = 5;
values.add(f); // Publication via thread-safe collection like Vector or Collections.synchronizedList(new ArrayList(...)) or ConcurrentHashMap?.
// Thread 2
if (values.size()>0){
System.out.println(values.get(0).x); // always 5 ?
}
Run Code Online (Sandbox Code Playgroud)
据我所知,Mathias可以根据JLS在某些JVM上打印出0.据我所知,它将始终打印5.
你有什么意见?
- 问候,德米特里
在这种情况下,您需要在将对象提供给其他线程时使用安全发布惯用法,即(来自Java Concurrency in Practice):
- 从静态初始化程序初始化对象引用;
- 将对它的引用存储到volatile字段或AtomicReference中;
- 将对它的引用存储到正确构造的对象的最终字段中; 要么
- 将对它的引用存储到由锁正确保护的字段中.
如果使用安全发布,则无需声明字段volatile
.
但是,如果你不使用它,声明字段volatile
(理论上)将无济于事,因为由volatile
一侧产生的内存障碍:易失性写入可以在其后的非易失性操作中重新排序.
因此,volatile
在以下情况下确保正确性:
class Foo {
public int x;
}
volatile Foo foo;
// Thread 1
Foo f = new Foo();
f.x = 42;
foo = f; // Safe publication via volatile reference
// Thread 2
if (foo != null)
System.out.println(foo.x); // Guaranteed to see 42
Run Code Online (Sandbox Code Playgroud)
但在这种情况下不起作用:
class Foo {
public volatile int x;
}
Foo foo;
// Thread 1
Foo f = new Foo();
// Volatile doesn't prevent reordering of the following actions!!!
f.x = 42;
foo = f;
// Thread 2
if (foo != null)
System.out.println(foo.x); // NOT guaranteed to see 42,
// since f.x = 42 can happen after foo = f
Run Code Online (Sandbox Code Playgroud)
从理论的角度来看,在第一个样本中存在一个传递发生在之前的关系
f.x = 42 happens before foo = f happens before read of foo.x
Run Code Online (Sandbox Code Playgroud)
在第二个示例f.x = 42
和读取foo.x
中没有通过before-before关系链接,因此它们可以按任何顺序执行.
您不需要在设置之前声明您的字段 volatile 其值start
在读取该字段的线程上调用该方法
原因是在这种情况下,设置与另一个线程中的读取处于先发生关系(如 Java 语言规范中定义)。
JLS的相关规则是:
但是,如果在设置该字段之前启动其他线程,则必须将该字段声明为易失性。JLS 不允许您假设线程在第一次读取该值之前不会缓存该值,即使在特定版本的 JVM 上可能是这种情况。
归档时间: |
|
查看次数: |
5158 次 |
最近记录: |