【问题标题】:sharing one instance of a hashmap between all instances of spring service class在 spring 服务类的所有实例之间共享一个 hashmap 实例
【发布时间】:2012-06-20 18:04:48
【问题描述】:

我打算创建一个实时计数器。所以一个用户可以增加特定键的计数器值。而另一个通过 ajax 请求(在循环中或使用某种长轮询方法)获取更新的计数值。我将使用一个 spring 控制器,它将注入服务类我可以执行以下操作,还是有更好的方法:

@Service
public MyService{

//instance variable in spring injected service class, not sure if this correct
static final Map<String, Integer> myMap;


public void add(String key){
  Integer count = myMap.get(key);
  count++;
  myMap.put(key, count);
}

//accessed via ajax loop (and controller), if value changes update display
public Integer getCount(String key){
  return myMap.get(key)
}

@PostConstruct
public load(){
  myMap = new HashMap<String, Integer>(10){{//initialize}};
}

编辑有几个答案,但不清楚哪个是最好的:同步添加方法?在另一个类(带注释的存储库)中创建地图并注入它?还有什么?

【问题讨论】:

    标签: java spring real-time


    【解决方案1】:

    您可以,但需要注意这些问题:

    • 地图最初是空的,但您从不检查空计数器;
    • add() 方法不会修改映射中的计数器。您需要在递增后将计数器放回地图中,因为 Integer 是不可变的。或者您需要在地图中存储可变计数器
    • 多个线程在没有任何同步的情况下访问地图,这将导致错误、不稳定的行为或异常
    • 如果您的应用集群在多台服务器之间,这种策略显然会失败

    【讨论】:

    • 我已经编辑掉了一些小问题,但是解决“多个线程正在访问地图而没有任何同步,这将导致错误、不稳定的行为或异常”的最佳方法是什么?
    • 在一个真正的应用程序中,有不止一个主线程可以访问你的共享资源(特别是在Web应用程序中),所以一个线程可以用“key”键放置对象A,而另一个线程将同时插入带有“key”键的对象 B。如果是地图,您可以使用java.util.concurrent.ConcurrentHashMap 来管理这些问题。
    • 正确使用 ConcurrentMap 存储 AtomicInteger 实例是一种解决方案。但是在实现任何东西之前,您确实需要了解所有潜在的线程问题。阅读 Java 并发实践
    • @JBNizet 我不能只创建一个类来包装地图、注释为存储库并注入它吗?
    • 是的,你可以,正如我在回答中所说的那样。但由于 bean 将被多个线程访问,因此您必须使其线程安全且正确。
    【解决方案2】:

    使用 ConcurrentHashMap

    public void add(String key){
        Integer count = myMap.get(key);
        count= count++;
        myMap.put(key, count);
    }
    

    【讨论】:

    • 这仍然是不正确的代码。它没有任何同步问题,但是您可能有三个线程同时调用 add 方法,并且值仅增加 1。
    • 是的,你是对的。我认为必须使方法同步?
    • 这是一种使代码正确的方法,前提是访问地图的其他方法也同步。但是如果这样做了,使用 ConcurrentMap 并没有比使用简单的 HashMap 有任何优势。
    • 同步服务方式是我们必须避免的事情
    【解决方案3】:

    Integer 类是不可变的。这意味着您不能对其进行修改。因此,为了增加计数,您必须在增加计数后将其放回到地图中:

    public void add(String key){
      Integer count = myMap.get(key);
      count++;
      myMap.put(key, count);
    }
    

    这带来的一个问题是线程安全。如果这个服务类将被多个线程同时访问,那么您必须确保以安全的方式访问它的数据。由于myMap 正在修改,并且HashMap 类不是线程安全的,因此您必须使其成为线程安全的。一种方法是使用Collections.synchronizedMap() 方法。这将自动使 Map 实例成为线程安全的。

    @PostConstruct
    public load(){
      myMap = new HashMap<String, Integer>(10){{//initialize}};
      myMap = Collections.synchronizedMap(myMap);
    }
    

    【讨论】:

    • 这将使Map线程安全,但非原子add()方法仍然不是线程安全的。
    • @nicholas.hauschild 哦,是的,这是真的。您必须使 add() 方法同步或将其主体包装在 synchronized(myMap) 块中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-26
    • 2017-10-19
    • 1970-01-01
    • 2021-07-04
    • 2012-12-17
    • 1970-01-01
    相关资源
    最近更新 更多