4
@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();
    }
}

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

4

3 回答 3

3

Java 内存模型保证在对 volatile 变量的写入和后续读取之间存在发生前的关系。换句话说,如果您写入 volatile 变量并随后读取相同的变量,则可以保证写入操作将是可见的,即使涉及多个线程:

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

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

您的getDeliverers方法从 volatile 变量中读取,因此它将看到对该行进行的最新写入操作deliverers = Collections.unmodifiableMap(deliverersMod);以及填充映射的先前操作。

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

于 2013-07-08T14:27:17.163 回答
1

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

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

于 2013-07-08T16:58:26.923 回答
0

这里的线程安全问题:

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

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

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

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

于 2013-07-08T07:00:44.117 回答