并发访问unmodifiableMap

时间:2013-07-04 14:00:46

标签: java java-ee concurrency unmodifiable

@Singleton
@LocalBean
@Startup
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class DeliverersHolderSingleton {

    private volatile Map<String, Deliverer> deliverers;

    @PostConstruct
    private void init() {
        Map<String, Deliverer> deliverersMod = new HashMap<>();
        for (String delivererName : delivererNames) {
            /*gettig deliverer by name*/
            deliverersMod.put(delivererName, deliverer);
        }
        deliverers = Collections.unmodifiableMap(deliverersMod);
    }

    public Deliverer getDeliverer(String delivererName) {
        return deliverers.get(delivererName);
    }

    @Schedule(minute="*", hour="*")
    public void maintenance() {
        init();
    }
}

Singleton用于存储数据。数据每分钟更新一次。 是否有可能从unmodifiableMap读取将是同步问题?是否有可能在init方法中重新排序并且发布集合的链接,但集合未完全填充?

3 个答案:

答案 0 :(得分:3)

Java内存模型保证there is a happens-before relationship between a write and a subsequent read to a volatile variable。换句话说,如果你写一个volatile变量并随后读取同一个变量,你就可以保证写操作可见,即使涉及多个线程:

  

在对该字段的每次后续读取之前发生对易失性字段(第8.3.1.4节)的写入。

它更进一步,并保证在写入操作之前发生的任何操作也将在读取点可见(由于程序顺序规则以及发生 - 之前关系是传递的事实)。

您的getDeliverers方法从volatile变量中读取,因此它将看到在deliverers = Collections.unmodifiableMap(deliverersMod);行上运行的最新写入以及填充地图的前面操作。

因此,您的代码是线程安全的,并且您的getDeliverers方法将根据地图的最新版本返回结果。

答案 1 :(得分:1)

根据此处http://g.oswego.edu/dl/jmm/cookbook.html找到的重新排序网格,第一个操作Normal Store无法重新排序,第二个操作为Volatile Store,因此在您的情况下,只要不可变的地图不是空的,不会有任何重新排序的问题。

此外,在易失性存储之前发生的所有写操作都是可见的,因此您不会看到任何发布问题。

答案 2 :(得分:0)

线程安全问题:

  • 来自HashMap的多次读取 - 是线程安全的,因为只要没有对集合的修改就不允许多次读取,并且不会发生对HashMap的写入,因为映射是unmodifiableMap()

  • deliverers上读/写 - 是线程安全的,因为所有java引用赋值都是原子的

我在这里看不到线程不安全的操作。

我想指出init() metod的名称具有误导性,它表明它在初始化期间被调用一次;我建议将其称为rebuild()recreate()