我有一个静态HashMap,它将缓存由唯一整数标识的对象;它将从多个线程访问。我将在不同的线程中运行多个HashmapUser
类型的实例,每个实例都希望使用相同的HashMap(这就是为什么它是静态的)。
通常,HashmapUsers
将从HashMap中检索。虽然它是空的,但它需要从数据库中填充。此外,在某些情况下,HashMap将被清除,因为它需要数据发生变化,需要重新填充。
所以,我只是将所有与Map同步的交互。但我并不认为这是安全的,聪明的,或者它适用于静态变量。
此线程的以下实现是否安全?有任何简化或改进的建议吗?
public class HashmapUser {
private static HashMap<Integer, AType> theMap = new HashSet<>();
public HashmapUser() {
//....
}
public void performTask(boolean needsRefresh, Integer id) {
//....
AType x = getAtype(needsRefresh, id);
//....
}
private synchronized AType getAtype(boolean needsRefresh, Integer id) {
if (needsRefresh) {
theMap.clear();
}
if (theMap.size() == 0) {
// populate the set
}
return theMap.get(id);
}
}
答案 0 :(得分:3)
实际上,它绝对不是线程安全的。 HashmapUsers
的每个实例都将使用不同的锁(this
),这没有任何用处。您必须同步对象,例如HashMap本身。
将getAtype
更改为:
private AType getAtype(boolean needsRefresh, Integer id) {
synchronized(theMap) {
if (needsRefresh) {
theMap.clear();
}
if (theMap.size() == 0) {
// populate the set
}
return theMap.get(id);
}
}
编辑:
请注意,您可以在任何对象上进行同步,前提是所有实例都使用同一对象进行同步。您可以在HashmapUsers.class
上进行同步,这也允许其他对象锁定对地图的访问权限(尽管通常最好使用私有锁)。
因此,只需将getAtype
方法设为静态即可,因为隐含锁定现在为HashMapUsers.class
而不是this
。但是,这会暴露您的锁定,这可能是您想要的也可能不是。
答案 1 :(得分:1)
不,这根本不起作用。
如果您没有指定锁定对象,例如声明方法synchronized
,隐式锁定将是实例。除非方法是静态的,否则锁将是类。由于存在多个实例,因此还存在多个锁,我怀疑是否需要。
您应该做的是创建另一个类,这是唯一可以访问HashMap
的类。
HashMap
的客户端(例如HashMapUser
)甚至不必知道存在同步。相反,应该通过正确的类来保证线程安全,HashMap
将客户端的同步隐藏起来。
这使您可以轻松地将其他客户端添加到HashMap
,因为同步对它们是隐藏的,否则您也必须在不同的客户端类型之间添加某种同步。
答案 2 :(得分:0)
我建议你选择ConcurrentHashMap
或SynchronizedMap
。
更多信息:http://crunchify.com/hashmap-vs-concurrenthashmap-vs-synchronizedmap-how-a-hashmap-can-be-synchronized-in-java/
ConcurrentHashMap
更适合高并发场景。此实现不会在整个对象上进行同步,而是以优化的方式进行同步,因此访问不同键的不同线程可以同时执行此操作。
SynchronizerMap
更简单,并且在对象级别上进行同步 - 对实例的访问是串行的。
我认为你需要表现,所以我认为你应该选择ConcurrentHashMap
。