Java 对不可修改映射的并发访问 @Singleton @本地豆 @启动 @ConcurrencyManagement(ConcurrencyManagementType.BEAN) 公共类投递商HolderSingleton{ 私人易变地图递送者; @施工后 私有void init(){ Map deliverresmod=new HashMap(); for(字符串传递名称:传递名称){ /*按名称获取交付者*/ DeliverMod.put(DeliverName,Deliver); } deliveriers=Collections.unmodifiableMap(deliveriersmod); } 公共传递器getDeliver(字符串deliverName){ 返回送货人。获取(送货人名称); } @时间表(分钟=“*”,小时=“*”) 公共空间维护(){ init(); } }
Singleton用于存储数据。数据每分钟更新一次。Java 对不可修改映射的并发访问 @Singleton @本地豆 @启动 @ConcurrencyManagement(ConcurrencyManagementType.BEAN) 公共类投递商HolderSingleton{ 私人易变地图递送者; @施工后 私有void init(){ Map deliverresmod=new HashMap(); for(字符串传递名称:传递名称){ /*按名称获取交付者*/ DeliverMod.put(DeliverName,Deliver); } deliveriers=Collections.unmodifiableMap(deliveriersmod); } 公共传递器getDeliver(字符串deliverName){ 返回送货人。获取(送货人名称); } @时间表(分钟=“*”,小时=“*”) 公共空间维护(){ init(); } },java,jakarta-ee,concurrency,unmodifiable,Java,Jakarta Ee,Concurrency,Unmodifiable,Singleton用于存储数据。数据每分钟更新一次。 是否可能,从不可修改的映射读取会导致同步出现问题?是否有可能在init方法中重新排序,并发布到集合的链接,但集合未完全填充?此处的线程安全问题: 从HashMap多次读取-是线程安全的,因为只要不修改集合,就允许多次读取,并且不会写入HashMap,因为该映射是不可修改的映射() 在交付程序上读/写是线程安全的,因为所有java引用分配都是原子的 我在这里看不到线程不安全的操作 我想指出的是,init()metod的名称有误导性,它表明在
是否可能,从不可修改的映射读取会导致同步出现问题?是否有可能在init方法中重新排序,并发布到集合的链接,但集合未完全填充?此处的线程安全问题:
- 从HashMap多次读取-是线程安全的,因为只要不修改集合,就允许多次读取,并且不会写入HashMap,因为该映射是
不可修改的映射()
- 在
交付程序上读/写是线程安全的,因为所有java引用分配都是原子的
我想指出的是,
init()
metod的名称有误导性,它表明在初始化过程中只调用了一次;我建议将其称为rebuild()
或recreate()
,Java内存模型保证了这一点。换句话说,如果您写入一个volatile变量并随后读取该变量,那么即使涉及多个线程,您也可以保证写入操作是可见的:
对易失性字段(§8.3.1.4)的写入发生在该字段的每次后续读取之前
它更进一步,并保证在写入操作之前发生的任何操作也将在读取点可见(这要归功于程序顺序规则以及“发生在之前”关系是可传递的这一事实)
您的getDeliveriers
方法从volatile变量中读取,因此它将看到在Deliveriers=Collections.unmodifiableMap(deliverierMod)行上操作的最新写操作代码>以及填充映射的前面操作
因此,您的代码是线程安全的,getDeliveriers
方法将根据地图的最新版本返回结果。根据此处找到的重新排序网格,第一个操作是普通存储
,第二个操作是易失性存储
,因此在您的情况下,只要不可变映射不是null,就不会有任何重新排序问题
另外,在volatile存储之前发生的所有写入操作都将可见,因此您不会看到任何发布问题。我想关键问题是从@PostConstruct
方法返回的数据是否是在那里设置的数据的“安全发布”。如果我们假设不是,那么半构造的bean将被公开,这将是一个相当大的设计缺陷,会在多个地方咬到你。deliverier
的易变性会阻止重新排序,你的类应该按预期工作(假设按名称获取deliverier是一个线程安全的操作)。由于重新排序,引用是否可能在集合初始化之前发布?@shurik2533如果发布了,我相信编程没有任何意义-@Dariusz有一些重新排序不会影响单线程模式下的执行,但会影响多线程程序。例如,移动deliveriers=Collections.unmodifiableMap(deliveriersmod)代码>在循环之前。@Dariusz不是真的-不可修改的映射是原始映射上的视图。如果更改原始地图本身,则不可修改的地图也将反映更改。@assylias您对地图的看法是正确的。我仍然认为unmodifiableMap()
方法调用不能在循环之前被重新排序。如果该方法修改了地图怎么办?Java在编译时不可能知道这一点。
@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();
}
}