Java 并发实践:3.5.4 Effectively immutable objects: Do we need Thread-Safe Collection containers for effectively immutable objects
Java Concurrency in Practice: 3.5.4 Effectively immutable objects: Do we need Thread-Safe Collection containers for effectively immutable objects
第 3.5.4 节讨论了有效不可变对象,即一旦对象被安全且完整地构建,其状态将不会被任何代码路径的任何代码更改。
Goetz 爵士举了一个例子:
For example, Date
is mutable, but if you use it as if it were
immutable you may be able to eliminate the locking that would
otherwise be required when shared[sharing] a Date across threads.
Suppose you want to maintain a Map storing the last login time of each
user:
public Map<String, Date> lastLogin =
Collections.synchronizedMap(new HashMap<String, Date>());
If the Date
values are not modified after they are placed in the
Map
, then the synchronization in the synchronizedMap
implementation is sufficient to publish the Date
values safely, and
no additional synchronization is needed when accessing them.
我无法理解的一点是,当我们可以简单地使用不安全的 Map
时,为什么我们要使用 synchronizedMap
并承担其内部锁定的额外开销,因为毕竟我们会在其中放置有效的不可变 Date
对象——这意味着,一旦正确且完全构建和发布,它就不会再发生变化。因此,即使 Map
本身是不安全的,在任何代码路径中也不会有任何代码可以同时改变任何 Date
实例,而其他线程已从不安全中检索到它Map
.
总而言之,有效不可变对象的前提不需要任何线程安全容器,因为我们不应该在任何代码路径中为有效不可变对象添加任何修改器代码。
如果您使用 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);
}
原文中的关键词是,"fully constructed and published." "Published",特指让一个线程创建的对象对其他线程可见,而当对象不是真正不可变,那么一定要安全(Google"Java safe publication").
如果没有同步,Java 不保证一个线程对变量所做的更新将被其他线程看到,或者以什么顺序看到更新。
在大多数计算机体系结构中,为所有线程提供共享内存的一致视图相对昂贵。通过不要求线程具有一致的视图,除非显式同步,Java 允许线程在需要时获得一致的视图,或者在不需要时获得最佳性能。
此外,以上所有内容都忽略了一个非常真实的可能性,即程序可能出于 其他 原因(例如,防止同时更新破坏地图本身。)
第 3.5.4 节讨论了有效不可变对象,即一旦对象被安全且完整地构建,其状态将不会被任何代码路径的任何代码更改。
Goetz 爵士举了一个例子:
For example,
Date
is mutable, but if you use it as if it were immutable you may be able to eliminate the locking that would otherwise be required when shared[sharing] a Date across threads. Suppose you want to maintain a Map storing the last login time of each user:public Map<String, Date> lastLogin = Collections.synchronizedMap(new HashMap<String, Date>());
If the
Date
values are not modified after they are placed in theMap
, then the synchronization in thesynchronizedMap
implementation is sufficient to publish theDate
values safely, and no additional synchronization is needed when accessing them.
我无法理解的一点是,当我们可以简单地使用不安全的 Map
时,为什么我们要使用 synchronizedMap
并承担其内部锁定的额外开销,因为毕竟我们会在其中放置有效的不可变 Date
对象——这意味着,一旦正确且完全构建和发布,它就不会再发生变化。因此,即使 Map
本身是不安全的,在任何代码路径中也不会有任何代码可以同时改变任何 Date
实例,而其他线程已从不安全中检索到它Map
.
总而言之,有效不可变对象的前提不需要任何线程安全容器,因为我们不应该在任何代码路径中为有效不可变对象添加任何修改器代码。
如果您使用 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);
}
原文中的关键词是,"fully constructed and published." "Published",特指让一个线程创建的对象对其他线程可见,而当对象不是真正不可变,那么一定要安全(Google"Java safe publication").
如果没有同步,Java 不保证一个线程对变量所做的更新将被其他线程看到,或者以什么顺序看到更新。
在大多数计算机体系结构中,为所有线程提供共享内存的一致视图相对昂贵。通过不要求线程具有一致的视图,除非显式同步,Java 允许线程在需要时获得一致的视图,或者在不需要时获得最佳性能。
此外,以上所有内容都忽略了一个非常真实的可能性,即程序可能出于 其他 原因(例如,防止同时更新破坏地图本身。)