这个HashMap使用线程安全吗?

时间:2015-08-25 17:51:47

标签: java multithreading

我有一个静态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);
  }
}

3 个答案:

答案 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)

我建议你选择ConcurrentHashMapSynchronizedMap。 更多信息:http://crunchify.com/hashmap-vs-concurrenthashmap-vs-synchronizedmap-how-a-hashmap-can-be-synchronized-in-java/

ConcurrentHashMap更适合高并发场景。此实现不会在整个对象上进行同步,而是以优化的方式进行同步,因此访问不同键的不同线程可以同时执行此操作。

SynchronizerMap更简单,并且在对象级别上进行同步 - 对实例的访问是串行的。

我认为你需要表现,所以我认为你应该选择ConcurrentHashMap