当前位置: 首页 > 知识库问答 >
问题:

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

韩高峯
2023-03-14

我正在使用以下字段:

private DateDao dateDao;

private volatile Map<String, Date> dates;

public Map<String, Date> getDates() {
    return Collections.unmodifiableMap(dates);
}

public retrieveDates() {
     dates = dateDao.retrieveDates();
}

哪里

public interface DateDao {
    //Currently returns HashMap instance
    public Map<String, Date> retrieveDates();
}

这样发布日期图安全吗?我的意思是,易失性字段意味着对一个字段的引用不会缓存在CPU寄存器中,并且在任何时候访问它都可以从内存中读取。

因此,我们不妨为映射的状态读取一个过时的值,因为HashMap不执行任何同步。

这样做安全吗?

UPD:例如,假设DAo方法以以下方式实现:

public Map<String, Date> retrieveDates() {
    Map<String, Date> retVal = new HashMap<>();
    retVal.put("SomeString", new Date());
    //ad so forth...
    return retVal;
}

可以看出,道方法不做任何同步,并且HashMapDate都是可变的,不是线程安全的。现在,我们已经创建并发布了它们,如上所示。是否保证从另一个线程的日期中读取的任何后续读取不仅会观察到对Map对象的正确引用,还会观察到它的"新鲜"状态。

我不确定线程是否不能观察到一些过时的值(例如dates.get(“SomeString”)返回null


共有2个答案

吴建中
2023-03-14

据我所知,声明地图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 retrieveDates() {
   synchronized(_lock) {
     dates = dateDao.retrieveDates();
   }
}

这可以通过volatile提供读写器/写写器同步(但请注意,写器没有优先级,即如果读写器正在获取地图,写器将不得不等待)和“数据新鲜度”。此外,这是一种非常基本的方法,还有其他方法可以实现相同的功能(例如,Locks和Semaphores),但在大多数情况下,这对我来说是个好办法。

程磊
2023-03-14
匿名用户

我想你在问两个问题:

>

 dates = dateDao.retrieveDates();

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

字段是否为易失性与这两个问题无关。使字段不稳定所做的唯一一件事是防止调用getDates的线程获得日期字段的过期值。即:

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,则不是,线程B将从#2而不是#1看到日期的值。

但这与我认为你要问的任何一个问题都无关。

否,retrieveDates中的代码无法看到dateDao返回的对象引用。在日期之前检索。retrieveDates已完成填写该地图。内存模型允许对语句进行重新排序,但:

...编译器可以对任一线程中的指令重新排序,前提是这不会影响该线程的独立执行

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

您展示的DAO代码永远无法修改它返回给您的映射,因为它没有它的副本,所以我们不需要担心DAO。

在代码中,没有显示任何修改日期内容的内容。如果代码没有修改日期的内容,那么就不需要同步,因为映射是不变的。您可能希望在获取日期时,而不是在返回日期时,在只读视图中包装日期,以此作为保证:

dates = Collection.unmodifiableMap(dateDao.retrieveDates());

如果你的代码确实修改了日期你没有显示的地方,那么是的,因为集合可能会出现问题。unmodifiableMap不同步映射操作。它只是创建了一个只读视图。

如果希望确保同步,则需要将日期包装到集合中。同步地图实例:

dates = Collections.synchronizedMap(dateDao.retrieveDates());

然后,代码中对它的所有访问都将同步,并且通过返回的只读视图对它的所有访问也将同步,因为它们都通过同步映射。

 类似资料:
  • 来自C/C++,我对Java中的volatile对象行为有点困惑。 null faik,volatile意味着b引用的“book对象”应该在主内存中。编译器可能在内部实现引用作为指针,因此b指针可能位于缓存中。我的理解是,volatile是对象的限定符,而不是引用/指针的限定符。 问题是:在使用方法中,本地引用不是易失性的。这个“本地”引用会不会把底层的Book对象从主存带到缓存中,实质上使对象不

  • 我已经读到,使引用变量易失性,并不会使其内部字段易失性。但我尝试了下面的示例,其中看起来易失性也应用于类的内部字段。 使用者java:-//字段“flag”设置为true的用户类。 MyRunnableThread1。java:- 在这里,我将“user”设置为volatile,而不是将其内部字段“flag”设置为volatile 子线程在“while(this.user.isFlag())”处连

  • 问题内容: 我需要保存一个用户模型,例如: 今天,我使用一个Set:users 在这个Set中,我有一个类似于user:alan的 成员在这个成员中,我上面有哈希 这很好,但是我只是想知道是否可以使用以下方法代替上述方法: 仍使用用户集(以轻松获取用户(成员)列表) 在此集中,仅使用键/值存储,例如: 键:alan值:上述用户哈希的字符串化版本 这样,检索记录将变得更加容易(然后我将不得不使用JS

  • C11 6.7.3类型限定符,第7段规定: 这里,指针的类型为,但我关心的是当实际指向的对象是非易失性的时会发生什么,特别是编译器是否可以将从对的单个访问转换为以下形式的两个访问: 这显然会使代码不正确。因此,目标是确定是否所有这样的指向对象实际上都需要。

  • 问题内容: 我正在尝试优化我的Elasticsearch方案。 我有一个URL字段-我不想查询或过滤它,而只是检索它。 我的理解是,定义为的字段未建立索引,但仍存储在索引中。(请参阅http://www.slideshare.net/nitin_stephens/lucene- basics中的 幻灯片5 )这应该与Lucene UnIndexed相匹配,对吗? 这使我感到困惑,是否有一种方法可以

  • 问题内容: 我有一个JSON对象,说: 我想将其绑定到Java对象中,例如: 如何将JSON 的值设置为Java对象中的字段? 我尝试使用来注释字段,但是那样行不通。 问题答案: 这不是完美的方法,但这是我能找出的最优雅的方法。