在Java中,什么时候可以“逃避”不使用synchronized对多个并发线程进行读/写的变量?
我读到了一些令人惊讶的并发错误:double-checked-locking和hash-maps,并且在共享读/写情况下一直默认使用同步,但是,我开始怀疑什么时候没有.
例如,我可以使用什么样的一般规则来确定何时从这样的方法中省略synchronized的实际安全性:
T getFoo() {
if (this.foo == null) {
this.foo = createFoo() // createFoo is always thread safe.
}
return this.foo;
}
哪里:
> T可能是基元或任意对象
> createFoo始终是线程安全的,可以多次调用,但是未指定.
> getFoo()可以“最终一致”.
如果T是像int这样的原语吗?整数怎么样?像String这样的简单对象呢?等等.
只有当您完全理解对底层硬件,JVM和应用程序的影响时,才会回答这个问题.如果可能的话,我仍然会推荐这种方法,从很多阅读和实验开始.
在实践中,您应该能够使用一些常用模式来最小化代码中同步方法或块的数量,而无需了解所述模式的所有复杂性.这不应该增加你的应用程序的风险,因为即使围绕synchronized的使用也有非平凡的细节,如果你理解了所有这些细节,你会问一个不同的问题.
尽管如此,您的里程可能会有所不同,特别是如果您所使用的常用模式的形式和实现没有得到您当地的并发大师的祝福.
足够的喋喋不休,给我一些例子
>使用线程安全的集合,实用程序和类型.
>这包括尽可能依赖java.util.concurrent.
>在JDK及其生态系统之外,还有大量额外的Java库和工具,但它们并不像“现在应用,稍后阅读”那样容易.
>使用volatile
>您可以使用volatile变量查看http://www.ibm.com/developerworks/library/j-jtp06197/的常见模式.
>使用安全初始化和安全发布
>你可以看看http://shipilev.net/blog/2014/safe-public-construction/,这可能是一个比初学者更适合的更高级的解释.
>我也在这里使用不可变类型.
>保持数据线程本地化.
>有点简单,而且经常适用.