您好
类下面是线程安全吗?
class ImmutablePossiblyThreadsafeClass<K, V> {
private final Map<K, V> map;
public ImmutablePossiblyThreadsafeClass(final Map<K, V> map) {
this.map = new HashMap<K, V>();
for (Entry<K, V> entry : map.entrySet()) {
this.map.put(entry.getKey(), entry.getValue());
}
}
public V get(K key) {
return this.map.get(key);
}
}
答案 0 :(得分:5)
如果这是整个类的定义,即没有其他的setter / modifier方法,那么map本身的用法是线程安全的:
final
,(请注意,这只是指地图的结构 - 地图中的各个元素可能仍然不安全。)
关于在构造过程中同时修改构造函数参数没有保护类的声明,我倾向于同意已经指出
的其他人(包括@Bozho)。ConcurrentHashMap
作为@Grundlefleck建议 - 我检查了源代码,其构造函数只调用了putAll()
,它没有防范这个),答案 1 :(得分:3)
当您复制构造函数参数的内容时,可能会出现问题。但是你无法做任何事情来防止这种情况发生。另一个潜在的问题是可变键。
此外,您可以使用以下命令替换构造函数:
public ImmutablePossiblyThreadsafeClass(final Map<K, V> map) {
this.map = new HashMap<K, V>(map);
}
答案 2 :(得分:1)
是的,它是线程安全的。它是不可变的,因此任何线程都不能进行任何会干扰另一个线程结果的更改。
注意:如果在构造过程中修改了地图,您将无法获得您认为已通过的内容,但之后访问地图时,它将是线程安全的。
答案 3 :(得分:1)
假设在构造之后没有任何内容改变地图字段(因为你已经将它命名为“不可变”,我想这就是这个想法)并且在构造期间不会改变作为构造函数参数提供的映射,这是一个线程安全的类。 / p>
请注意,除了迭代之外,还有更简单的方法可以将条目从一个地图复制到另一个地图。您可能还想查看Collections中的一些方法来创建集合的不可变版本。当然,您首先必须复制地图,因为底层地图仍然可以更改。 “不可修改的*”方法仅返回集合的视图。
答案 4 :(得分:1)
是和否,对于“线程安全”的不同值(对不起的答案)。构造不是线程安全的,但是一旦构造函数完成,并假设它已经正确发生,它将是不可变的和线程安全的。
在构建过程中,map
参数可能会被调用map.entrySet
与entry.getKey
和entry.getValue
之间的另一个线程修改。来自Map.entrySet
的javadoc。这可能导致未定义的行为。
来自java.util.Map
的{{1}} javadoc:
返回映射的set视图 包含在这张地图中。每个元素 返回的集合是Map.Entry。该 set由地图支持,因此更改 到地图都反映在集合中, 反之亦然。如果地图被修改 而对集合的迭代是在 进度,迭代的结果 未定义。该集支持 元素删除,删除 来自地图的相应映射, 通过Iterator.remove,Set.remove, removeAll,retainAll和clear 操作。它不支持 添加或添加所有操作。
因此,根据谁有对地图的引用,以及它们如何跨线程操作它,以及复制元素需要多长时间,您有不确定的行为,并且可能在程序的不同执行中有不同的行为。
关于确保从此类中正确构造,似乎没什么可做的(例如,因为entrySet
有类似的问题)。如果你的代码的其余部分可以确保给构造函数一个线程安全的映射,那么它也是线程安全的,但这比你提出的范围更宽。
然而,一旦构造函数完成,并且它被安全地发布[1]它将是不可变的和线程安全的 - 尽管只是在浅层 - 如果键或条目是可变的,这 taints 你的班级,既不是一成不变的,也不是线程安全的。因此,如果您的密钥是ConcurrentHashMap
并且您的值是String
,那么它将是不可变的,因此是线程安全的。但是,如果您的键是一个带有setter的bean类对象,并且您的值是其他可变集合类型,那么您的类不是不可变的。我倾向于将此标记为“乌龟一直向下”状态。
基本上,您的类可能不可变且线程安全,在您的问题未涵盖的某些超范围条件下。
[1]您的类目前将安全发布,但是,例如,如果它成为嵌套类,或者您在构造函数中将Integer
引用传递给另一个方法,则这可能会成为不安全的发布。
答案 5 :(得分:0)
在声明字段时使用ConcurrentHashMap
代替HashMap
和ConcurrentMap
,它应该是。
答案 6 :(得分:0)
类本身 是线程安全的。
类所包含的地图内容不是。客户可以在获得后更改条目。它是否使整个类的线程安全取决于此类与V之间的逻辑关系。
答案 7 :(得分:0)
我认为它的方式是线程安全的。您已创建(有效)输入map
的副本,并且仅对其执行只读操作。我总是认为不可变类本质上是线程安全的。但请注意,除非您的K
和V
类是不可变的,否则您仍可能遇到线程安全问题。
您也可以将其替换为:
Map<K, V> m = Collections.unmodifiableMap(Collections.synchronizedMap(map));