private volatile Service service; public void setService(Service service) { this.service = service; } public void doWork() { service.doWork(); }
被修改的字段标记为volatile,其值不依赖于以前的状态.因此,这是正确的多线程代码(不要在一分钟内麻烦服务实现).
据我所知,读取易失性变量就像进入锁一样,从内存可见性的角度来看.这是因为正常变量的读取不能用读取volatile变量来重新排序.
这是否意味着以下代码是正确的?
private volatile boolean serviceReady = false; private Service service; public void setService(Service service) { this.service = service; this.serviceReady = true; } public void doWork() { if ( serviceReady ) { service.doWork(); } }
解决方法
原子性不是一个问题,有或没有volatile(对对象引用的写入是原子的),所以你可以以任何方式将关系列表从这个关系列表中传出 – 唯一的问题是更改的可视性和排序的“正确性”.
对volatile变量的任何写入都将设置一个“发生之前”关系(新的Java内存模型的关键概念,如JSR-133中所述),同时可以读取相同的变量.这意味着阅读线程必须能够看到写入线程可见的所有内容:也就是说,在写入时它必须至少看到所有具有’current’值的变量.
我们可以通过查看section 17.4.5 of the Java Language Specification详细解释这一点,具体说明如下要点:
>“如果x和y是同一个线程的动作,而x在程序顺序中出现在y之前,那么hb(x,y)”(即,同一个线程上的动作不能以与程序顺序不一致的方式重新排序)
>“写入易失性字段(§8.3.1.4)发生在每个后续读取该字段之前.” (这是澄清的文本,解释了易失性字段的写即时读取是同步点)
>“如果hb(x,y)和hb(y,z),则hb(x,z)”(发生前的传递性)
所以在你的例子中:
>写入’service'(a)发生在写入’serviceReady'(b)之前,由于规则1
>由于规则2,写入“serviceReady”(b)发生在相同(c)的读取之前
因此,(a)发生在(c)(第3规则)之前
这意味着您确保’service’设置正确,在这种情况下,一旦serviceReady为true.
您可以使用几乎完全相同的示例看到一些好的写作,一个在IBM DeveloperWorks – 参见“新的易失性保证”:
values that were visible to A at the time that V was written are guaranteed now to be visible to B.
和一个在the JSR-133 FAQ,由JSR的作者写道:
Thus,if the reader sees the value true for v,it is also guaranteed to see the write to 42 that happened before it. This would not have been true under the old memory model. If v were not volatile,then the compiler could reorder the writes in writer,and reader’s read of x might see 0.