@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方法中重新排序并且发布集合的链接,但集合未完全填充?
答案 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()
。