[英]Storing object reference into a volatile field
我正在使用以下字段:
private DateDao dateDao;
private volatile Map<String, Date> dates;
public Map<String, Date> getDates(){
return Collections.unmodifiableMap(dates);
}
public retireveDates(){
dates = dateDao.retrieveDates();
}
哪里
public interface DateDao{
//Currently returns HashMap instance
public Map<String, Date> retrieveDates();
}
这样发布日期地图是否安全? 我的意思是,易失性字段意味着对该字段的引用不会被缓存在CPU寄存器中,并且在每次访问它时都会从内存中读取。
因此,我们最好还是读取state of the map
陈旧值,因为HashMap
不会进行任何同步。
这样做安全吗?
UPD:例如,假定DAo方法以以下方式实现:
public Map<String, Date> retrieveDates(){
Map<String, Date> retVal = new HashMap<>();
retVal.put("SomeString", new Date());
//ad so forth...
return retVal;
}
可以看出,Dao方法不执行任何同步,并且HashMap
和Date
都是可变的并且不是线程安全的。 现在,我们已经创建并发布了它们,如上所示。 是否保证从另一个线程的dates
任何后续读取将不仅观察到对Map
对象的正确引用,而且还观察到其“新鲜”状态。
我不确定线程是否不能观察到一些过时的值(例如, dates.get("SomeString")
返回null
)
据我所知,声明地图volatile
将不会同步其访问(即读者可以在dao更新地图时阅读该地图)。 但是,它保证了映射位于共享内存中,因此每个线程在每个给定时间都将在其中看到相同的值。 当我需要同步和更新时,通常会使用锁定对象,该对象类似于以下内容:
private DateDao dateDao;
private volatile Map<String, Date> dates;
private final Object _lock = new Object();
public Map<String, Date> getDates(){
synchronized(_lock) {
return Collections.unmodifiableMap(dates);
}
}
public retireveDates() {
synchronized(_lock) {
dates = dateDao.retrieveDates();
}
}
这提供了读取器/写入器同步(但是请注意,写入器没有优先级,即,如果读取器正在读取地图,则写入器将不得不等待)和通过volatile
“刷新数据”。 而且,这是一个非常基本的方法,还有其他实现相同功能的方法(例如Lock
和Semaphores
),但是在大多数情况下,这对我有用。
我认为您在问两个问题:
有了DAO代码,使用它的代码是否有可能使用它在此处得到的对象引用:
dates = dateDao.retrieveDates();
在引用的dateDao.retrieveDates
方法添加到该对象之前。 例如,内存模型的语句重新排序语义是否允许retrieveDates
方法在最后一次put
等完成之前返回引用?
一旦你的代码有dates
的参考,是否有与不同步访问,问题dates
在你的代码,也通过它的只读视图你回报getDates
。
您的领域是否volatile
与这些问题无关。 这使你的领域唯一volatile
的作用是防止线程调用getDates
从获得你一个彻头彻尾的最新值dates
字段。 那是:
Thread A Thread B ---------- -------- 1. Updates `dates` from dateDao.retrieveDates 2. Updates `dates` from " " again 3. getDates returns read-only view of `dates` from #1
如果没有volatile
,则上述情况是可能的(但无害)。 如果使用volatile
,则不是,线程B将看到#2而不是#1中的dates
值。
但这与我认为您所问的任何一个问题都不相关。
不,你的代码retireveDates
(原文如此),看不到返回的对象引用dateDao.retrieveDates
之前dateDao.retrieveDates
在该地图完成灌装。 内存模型允许对语句进行重新排序,但是:
...编译器可以在不影响单独执行该线程的情况下,对每个线程中的指令重新排序
(我的重点是。)在dateDao.retrieveDates
之前返回对代码的引用显然会dateDao.retrieveDates
影响线程的执行。
您显示的DAO代码永远不会修改它返回给您的地图,因为它不会保留它的副本,因此我们不必担心DAO。
在您的代码中,您没有显示任何修改dates
内容的内容。 如果您的代码没有修改dates
的内容,则由于地图是不变的,因此无需同步。 您可能希望通过在获取 dates
而不是返回dates
时将dates
包装在只读视图中来确保这一点:
dates = Collection.unmodifiableMap(dateDao.retrieveDates());
如果您的代码确实在未显示的地方修改了dates
,那么是的,可能会出现麻烦,因为Collections.unmodifiableMap
不会执行任何同步地图操作的操作。 它只是创建一个只读视图。
如果要确保同步,则要将dates
包装在Collections.synchronizedMap
实例中:
dates = Collections.synchronizedMap(dateDao.retrieveDates());
然后,代码中对它的所有访问都将被同步,并且通过返回的只读视图对它的所有访问也将被同步,因为它们全部都通过了同步映射。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.