Java Concurrency in Practice:3.5.4有效的不可变对象:我们是否需要Thread-Safe Collection容器来实现有效的不可变对象

时间:2015-11-14 17:28:57

标签: java multithreading concurrency immutability

第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代码。

2 个答案:

答案 0 :(得分:4)

如果您使用un-synchronized mutable map并在threads内分享,那么您将遇到两个thread-safety问题:visibilityatomicityThread-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>