繁体   English   中英

将对象引用存储到易失性字段中

[英]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方法不执行任何同步,并且HashMapDate都是可变的并且不是线程安全的。 现在,我们已经创建并发布了它们,如上所示。 是否保证从另一个线程的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 “刷新数据”。 而且,这是一个非常基本的方法,还有其他实现相同功能的方法(例如LockSemaphores ),但是在大多数情况下,这对我有用。

我认为您在问两个问题:

  1. 有了DAO代码,使用它的代码是否有可能使用它在此处得到的对象引用:

     dates = dateDao.retrieveDates(); 

    引用的dateDao.retrieveDates方法添加到该对象之前。 例如,内存模型的语句重新排序语义是否允许retrieveDates方法在最后一次put等完成之前返回引用?

  2. 一旦你的代码有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值。

但这与我认为您所问的任何一个问题都不相关。

问题1

不,你的代码retireveDates (原文如此),看不到返回的对象引用dateDao.retrieveDates之前dateDao.retrieveDates在该地图完成灌装。 内存模型允许对语句进行重新排序,但是:

...编译器可以在不影响单独执行该线程的情况下,对每个线程中的指令重新排序

(我的重点是。)dateDao.retrieveDates之前返回对代码的引用显然会dateDao.retrieveDates影响线程的执行。

问题2

您显示的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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM