将对象引用存储到易失性字段中
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 retrieveDates() {
dates = dateDao.retrieveDates();
}
在哪里
public interface DateDao {
//Currently returns HashMap instance
public Map<String, Date> retrieveDates();
}
这样发布日期地图安全吗?我的意思是,volatile 字段意味着对字段的引用不会缓存在 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")
returns null
)
据我所知,声明地图 volatile
不会同步其访问(即 readers 可以在 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();
}
}
这提供了 readers/writers 同步(但请注意,作者没有优先级,即如果 reader 正在获取地图,作者将不得不等待)和 'data freshness' 通过 volatile
。此外,这是一种非常基本的方法,还有其他方法可以实现相同的功能(例如 Lock
s 和 Semaphores
),但大多数时候这对我有用。
我认为你在问两个问题:
鉴于该 DAO 代码,您使用它的代码是否可以使用它在此处获取的对象引用:
dates = dateDao.retrieveDates();
before 引用的 dateDao.retrieveDates
方法已完成添加到该对象。例如,内存模型的语句重新排序语义是否允许 retrieveDates
方法在最后一个 put
(等)完成之前 return 引用?
- 一旦您的代码具有
dates
引用,您的代码中是否存在对 dates
的非同步访问以及通过 read-only 视图的问题 return 来自 getDates
.
你的领域是否 volatile
与这两个问题都没有关系。使您的字段 volatile
所做的唯一一件事是阻止调用 getDates
的线程为您的 dates
字段获取 out-of-date 值。即:
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
不,您在 retrieveDates
中的代码无法在 dateDao.retrieveDates
填写该地图之前看到 dateDao.retrieveDates
编辑的对象引用 return。 memory model 允许重新排序语句,但是:
...compilers are allowed to reorder the instructions in either thread, when this does not affect the execution of that thread in isolation
(我的重点。)在dateDao.retrieveDates
之前返回对代码的引用显然会影响隔离线程的执行。
问题 2
您显示的 DAO 代码永远无法修改它 return 给您的映射,因为它不保留它的副本,因此我们无需担心 DAO。
在你的代码中,你没有显示任何修改dates
内容的内容。如果你的代码没有修改 dates
的内容,那么就不需要同步,因为地图是不变的。当您 获取 时,您可能希望通过将 dates
包装在 read-only 视图中来保证这一点,而不是当您 return 它时:
dates = Collection.unmodifiableMap(dateDao.retrieveDates());
如果您的代码 修改了 dates
某个您没有显示的地方,那么是的,可能会出现问题,因为 Collections.unmodifiableMap
不会同步地图操作。它只是创建一个 read-only 视图。
如果您想确保同步,您需要将 dates
包装在 Collections.synchronizedMap
实例中:
dates = Collections.synchronizedMap(dateDao.retrieveDates());
然后在您的代码中对它的所有访问都将被同步,并且通过您 return 的 read-only 视图对它的所有访问也将被同步,因为它们都经过同步映射。
我正在使用以下字段:
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();
}
这样发布日期地图安全吗?我的意思是,volatile 字段意味着对字段的引用不会缓存在 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")
returns null
)
据我所知,声明地图 volatile
不会同步其访问(即 readers 可以在 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();
}
}
这提供了 readers/writers 同步(但请注意,作者没有优先级,即如果 reader 正在获取地图,作者将不得不等待)和 'data freshness' 通过 volatile
。此外,这是一种非常基本的方法,还有其他方法可以实现相同的功能(例如 Lock
s 和 Semaphores
),但大多数时候这对我有用。
我认为你在问两个问题:
鉴于该 DAO 代码,您使用它的代码是否可以使用它在此处获取的对象引用:
dates = dateDao.retrieveDates();
before 引用的 dateDao.retrieveDates
方法已完成添加到该对象。例如,内存模型的语句重新排序语义是否允许 retrieveDates
方法在最后一个 put
(等)完成之前 return 引用?
- 一旦您的代码具有
dates
引用,您的代码中是否存在对dates
的非同步访问以及通过 read-only 视图的问题 return 来自getDates
.
你的领域是否 volatile
与这两个问题都没有关系。使您的字段 volatile
所做的唯一一件事是阻止调用 getDates
的线程为您的 dates
字段获取 out-of-date 值。即:
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
不,您在 retrieveDates
中的代码无法在 dateDao.retrieveDates
填写该地图之前看到 dateDao.retrieveDates
编辑的对象引用 return。 memory model 允许重新排序语句,但是:
...compilers are allowed to reorder the instructions in either thread, when this does not affect the execution of that thread in isolation
(我的重点。)在dateDao.retrieveDates
之前返回对代码的引用显然会影响隔离线程的执行。
问题 2
您显示的 DAO 代码永远无法修改它 return 给您的映射,因为它不保留它的副本,因此我们无需担心 DAO。
在你的代码中,你没有显示任何修改dates
内容的内容。如果你的代码没有修改 dates
的内容,那么就不需要同步,因为地图是不变的。当您 获取 时,您可能希望通过将 dates
包装在 read-only 视图中来保证这一点,而不是当您 return 它时:
dates = Collection.unmodifiableMap(dateDao.retrieveDates());
如果您的代码 修改了 dates
某个您没有显示的地方,那么是的,可能会出现问题,因为 Collections.unmodifiableMap
不会同步地图操作。它只是创建一个 read-only 视图。
如果您想确保同步,您需要将 dates
包装在 Collections.synchronizedMap
实例中:
dates = Collections.synchronizedMap(dateDao.retrieveDates());
然后在您的代码中对它的所有访问都将被同步,并且通过您 return 的 read-only 视图对它的所有访问也将被同步,因为它们都经过同步映射。