Java 这个HashMap使用线程安全吗?
我有一个静态HashMap,它将缓存由唯一整数标识的对象;它将从多个线程访问。我将在不同的线程中运行多个类型为Java 这个HashMap使用线程安全吗?,java,multithreading,Java,Multithreading,我有一个静态HashMap,它将缓存由唯一整数标识的对象;它将从多个线程访问。我将在不同的线程中运行多个类型为HashmapUser的实例,每个实例都希望使用相同的HashMap(这就是为什么它是静态的) 通常,HashmapUsers将从HashMap中检索。但是如果它是空的,则需要从数据库填充它。此外,在某些情况下,HashMap将被清除,因为它需要数据发生更改并且需要重新填充 所以,我只是将所有与地图的交互同步。但我不能肯定这是安全的,聪明的,或者它对静态变量有效 下面这个线程的实现是安全
HashmapUser
的实例,每个实例都希望使用相同的HashMap(这就是为什么它是静态的)
通常,HashmapUsers
将从HashMap中检索。但是如果它是空的,则需要从数据库填充它。此外,在某些情况下,HashMap将被清除,因为它需要数据发生更改并且需要重新填充
所以,我只是将所有与地图的交互同步。但我不能肯定这是安全的,聪明的,或者它对静态变量有效
下面这个线程的实现是安全的吗?有没有简化或改进的建议
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);
}
}
公共类HashmapUser{
私有静态HashMap theMap=newhashset();
公共HashmapUser(){
//....
}
public void performTask(布尔需要刷新,整数id){
//....
AType x=getAtype(需要刷新,id);
//....
}
私有同步AType getAtype(布尔需要刷新,整数id){
如果(需要刷新){
theMap.clear();
}
如果(theMap.size()=0){
//填充集合
}
返回map.get(id);
}
}
我建议您选择ConcurrentHashMap
或SynchronizedMap
。
更多信息请点击此处:
ConcurrentHashMap
更适合高并发性场景。此实现不会对整个对象进行同步,而是以优化的方式进行同步,因此访问不同密钥的不同线程可以同时进行同步
SynchronizerMap
更简单,在对象级别执行同步-对实例的访问是串行的
我认为您需要性能,因此我认为您可能应该选择
ConcurrentHashMap
我建议您选择ConcurrentHashMap
或SynchronizedMap
。
更多信息请点击此处:
ConcurrentHashMap
更适合高并发性场景。此实现不会对整个对象进行同步,而是以优化的方式进行同步,因此访问不同密钥的不同线程可以同时进行同步
SynchronizerMap
更简单,在对象级别执行同步-对实例的访问是串行的
我认为您需要性能,因此我认为您可能应该使用
ConcurrentHashMap
,因为它绝对不是线程安全的。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
。但是,这会暴露您的锁,这可能是您想要的,也可能不是您想要的。事实上,它绝对不是线程安全的。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
。但是,这会暴露您的锁,这可能是您想要的,也可能不是您想要的。不,这根本不起作用
如果不指定锁对象,例如declare方法synchronized
,则隐式锁将是实例。除非方法是静态的,否则锁将是类。因为有多个实例,所以也有多个锁,我怀疑这是需要的
您应该做的是创建另一个类,它是唯一可以访问HashMap
的类
HashMap
的客户端,例如HashMapUser
甚至不能意识到存在同步。相反,应该通过适当的类包装HashMap
来确保线程安全,从而对客户端隐藏同步
这使您可以轻松地将其他客户端添加到
HashMap
,因为同步对它们是隐藏的,否则您也必须在不同的客户端类型之间添加某种类型的同步。不,这根本不起作用
如果不指定锁对象,例如declare方法synchronized
,则隐式锁将是实例。除非方法是静态的,否则锁将是类。因为有多个实例,所以也有多个锁,我怀疑这是需要的
您应该做的是创建另一个类,它是唯一可以访问HashMap
的类
HashMap
的客户端,例如HashMapUser
甚至不能意识到存在同步。相反,应该通过适当的类包装HashMap
来确保线程安全,从而对客户端隐藏同步
这使您可以轻松地将其他客户端添加到