第3.5.4节讨论了有效的不可变对象,也就是说,一旦对象被安全地完全构造,其状态就不会被任何代码路径的代码所改变。
Sir Goetz举了一个例子:
例如,
Date
是可变的,但如果你使用它就像它一样 不可变的你可能能够消除那种锁定 否则在跨线程共享[共享]日期时需要。 假设您要维护一个存储每个登录时间的Map 用户:public Map<String, Date> lastLogin = Collections.synchronizedMap(new HashMap<String, Date>());
如果
Date
值在放入之后未被修改Map
,然后synchronizedMap
中的同步 实现足以安全地发布Date
值,并且 访问它们时不需要额外的同步。
我无法理解的一点是,为什么我们想要使用synchronizedMap
并承担其内部锁定的额外开销,因为我们可以简单地使用不安全的Map
,因为毕竟我们将在其中放置有效的不可变Date
对象 - 这意味着,一旦正确完整地构建和发布,它就不会再被变异了。因此,即使Map
本身不安全,任何代码路径中都没有代码可以同时改变任何Date
实例,而其他线程已从不安全检索它Map
。
总而言之,有效不可变对象的前提并不需要任何线程安全容器,因为我们应该在任何代码路径中都没有任何有效不可变对象的mutator代码。
答案 0 :(得分:4)
如果您使用un-synchronized
mutable map
并在threads
内分享,那么您将遇到两个thread-safety
问题:visibility
和atomicity
。 Thread-1
不知道Thread-2
是否删除了Map-Entry
,还是将其值替换为新的Date
对象。
// not atmoic and doesn't guarantee visiblity
if(map.contains(key)){
map.put(key,newDate);
}
答案 1 :(得分:1)
原始文本中的关键词是“完全构建和发布”。 “已发布”,特别是指使一个线程创建的对象对其他线程可见,并且当该对象真正不可变时,则必须安全地(谷歌“Java安全出版物”)。
如果没有同步,Java不保证其他线程可以看到一个线程所做变量的更新,或者看到更新的顺序。
在大多数计算机体系结构中,为所有线程提供共享内存的一致视图是相对昂贵的。通过不要求线程具有一致的视图,除非明确同步,Java允许线程在需要时获得一致的视图,或者在不需要时获得最佳性能。
此外,上述所有内容都忽略了程序可能需要根据其他原因同步访问Map的非常现实的可能性(例如,防止同时更新破坏Map本身。)< / p>