在这种情况下,我是否需要使用线程安全映射?

时间:2014-03-09 08:21:43

标签: java mysql multithreading concurrency concurrentmodification

我想定期用java从mysql加载一些系统信息,也许10秒一次,并用一个线程来完成这个,同时,有多个线程需要读取这些信息,有两种情况:< / p>

1,声明public static地图:

public  volatile static Map<String, String> info = new HashMap<String, String>();

// Context.java
// start a thread to load from mysql
private void load() {
    new Thread(new Runnable() {
        while(true) {
              // return a map
              info = loadFromDB();        // every time a new map 
              Thread.sleep(5 * 1000);     // sleep 5 seconds
           } 
     }).start();
}

// mutiple threads call this function
public String getConfig(String key) {
    return Context.map.get(key);
}

正如代码所示,每次线程从db加载信息时,它都是一个新的地图,我不需要更改map内容,这个线程是否安全?

2,也许我想要一个有序的地图,并且读取线程需要在地图中获取最大键,在这种情况下,我应该使用TreeMap还是ConcurrentSkipListMap

更新

使用Map更改volatile信息以避免使用cpu进行缓存。

3 个答案:

答案 0 :(得分:1)

java中的单字段赋值是原子的,所以如果你的线程在loadFromDB()方法调用中构建一个新的hashmap(所以它没有触及现有的info map),然后只需更改{{1}的值引用它创建的这个新映射的操作是正确的 - 任何其他并发线程都可以看到旧的info值或新的值 - 两者之间没有任何内容。

但有几点:

  1. 在这种情况下,您应该将info设置为volatile字段info,以避免在更改引用时将值缓存在处理器缓存中并且不更新的情况。
  2. 您编写的任何从信息图中读取值的代码都需要获取对地图的引用,并且只需要一次,并使用该引用进行操作。例如:
  3. 这没关系

    public static volatile Map...

    但这不行

    Map infoMap = info;
    value1 = infoMap.get("key1");
    //do something
    value2 = infoMap.get("key2");
    

    因为您可能正在从较新的信息地图中读取value2。

答案 1 :(得分:1)

对于具有共享内存的多线程应用程序,需要记住一条重要规则。关于Java内存模型,JLS7在17.4.5中说明:

  

[..]如果所有顺序一致的执行都没有数据竞争,[..]那么程序的所有执行都将依次为一致的

乍一看,这句话听起来很怪异。但是,它确切地告诉你你必须知道什么。在你的情况下,它意味着:

没有不稳定

如果从变量volatile的定义中删除限定符info,则此变量上存在数据竞争,因为两个线程可以访问变量同时,至少一个操作是写操作。这意味着,不能保证顺序一致性,这意味着很难解释。最有可能的是,您会看到各种奇怪的异常,因为读者线程会看到不一致的 HashMap

有易失性

如果您将限定符volatile添加到变量info的定义中,那么变量info上也没有数据竞争任何其他内存位置,因为没有内存位置可以同时由两个线程(至少有一个写操作)访问。请注意,对 volatile 变量的操作不是数据竞争

这意味着在所有执行中,所有操作似乎都按照与程序顺序一致的总顺序执行。这意味着,您的应用程序按预期工作。

答案 2 :(得分:0)

理论上是的,在实践中没有。

理论上,您需要使用并发映射,因为get的实现可能不是线程安全的。地图实现可以使用不安全的变量进行内部优化,或者对get调用重新哈希,或者执行可能导致某些线程问题的其他事情。

在实践中,据我所知,get调用是一个简单的数组查找,而是线程安全的。但是,这仅适用于特定实现(Oracle,Windows)上的当前Java版本,并且可能与其他地方不同。

由于在非并发映射上使用并发的成本非常低,我建议使用并发映射,即使只是怀疑它是否是线程安全的。