Java 使用不带readlock的writeLock安全吗?
假设这是一个单例实现:我是否保证只调用productCatalogLoader.load一次,并且不会出现空指针?有没有办法让这更简单Java 使用不带readlock的writeLock安全吗?,java,concurrency,Java,Concurrency,假设这是一个单例实现:我是否保证只调用productCatalogLoader.load一次,并且不会出现空指针?有没有办法让这更简单 private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private ProductCatalog productCatalog; public ProductCatalog get() { if (this.productCatalog == nul
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private ProductCatalog productCatalog;
public ProductCatalog get() {
if (this.productCatalog == null) {
reload();
}
return this.productCatalog;
}
public void reload() {
lock.writeLock().lock();
try {
if (this.productCatalog != null) return;
this.productCatalog = productCatalogLoader.load();
} finally {
lock.writeLock().unlock();
}
}
}
编辑:
这是一次相当成功的尝试,将更复杂的代码简化为一个简单的问题示例。有几个人意识到我的单身生活太复杂了;实际上还有一个石英计时器调用reload,但是reload实现与IRL有点不同。我得到了我需要的答案,打破了双重检查锁定。在我看来,让它更简单的最好方法是使用普通锁,除非你确实有很好的证据证明它造成了瓶颈:
private final Object lock = new Object();
private ProductCatalog productCatalog;
public ProductCatalog get() {
synchronized (lock) {
if (this.productCatalog == null) {
this.productCatalog = productCatalogLoader.load();
}
return this.productCatalog;
}
}
在绝大多数情况下,这已经足够好了,您不需要担心内存模型的技术细节
编辑:至于在不获取读锁的情况下读取写锁中更改的数据是否安全,我怀疑不安全,但我不想肯定地说。如果您小心地使用volatile变量进行双重检查锁定,那么在Java1.5+上使用普通和安全的方法有什么好处吗
private final Object lock = new Object();
private volatile ProductCatalog productCatalog;
public ProductCatalog get() {
if (this.productCatalog == null) {
synchronized (lock) {
if (this.productCatalog == null) {
this.productCatalog = productCatalogLoader.load();
}
}
}
return this.productCatalog;
}
我相信这是有效的Java2ndEdition中推荐的惰性初始化技术,适用于静态初始值设定项不足的情况。请注意,productCatalog必须是易失性的,才能正常工作。我认为,如果不去掉代码中的读锁,这就是您实际上缺少的。IMO,使其更简单的最佳方法是使用普通锁,除非您确实有充分的证据证明它会造成瓶颈:
private final Object lock = new Object();
private ProductCatalog productCatalog;
public ProductCatalog get() {
synchronized (lock) {
if (this.productCatalog == null) {
this.productCatalog = productCatalogLoader.load();
}
return this.productCatalog;
}
}
在绝大多数情况下,这已经足够好了,您不需要担心内存模型的技术细节
编辑:至于在不获取读锁的情况下读取写锁中更改的数据是否安全,我怀疑不安全,但我不想肯定地说。如果您小心地使用volatile变量进行双重检查锁定,那么在Java1.5+上使用普通和安全的方法有什么好处吗
private final Object lock = new Object();
private volatile ProductCatalog productCatalog;
public ProductCatalog get() {
if (this.productCatalog == null) {
synchronized (lock) {
if (this.productCatalog == null) {
this.productCatalog = productCatalogLoader.load();
}
}
}
return this.productCatalog;
}
我相信这是有效的Java2ndEdition中推荐的惰性初始化技术,适用于静态初始值设定项不足的情况。请注意,productCatalog必须是易失性的,才能正常工作。我认为,如果不取出代码中的读锁,这就是您缺少的内容。不,一般来说,这是不安全的。。。writelock仅保证在释放writelock之前,其他任何人都无法获得readlock或writelock。 不在代码中使用readlock类似于同步了reload方法而不是get方法。我有点同意Jon Skeet的观点,但这在很大程度上取决于用法 如果您有很多线程调用get或多或少是同时进行的,那么您可能想要一个readlock,而不是同步它 编辑:顺便说一句,在有效的Java第二版中有一个很好的例子。。。
我还必须在这里纠正我自己,考虑到上下文,如果以一种智能的方式进行同步,那么同步也会工作得很好,而writelock可能也会在这种上下文中工作。不,一般来说,它不安全。。。writelock仅保证在释放writelock之前,其他任何人都无法获得readlock或writelock。 不在代码中使用readlock类似于同步了reload方法而不是get方法。我有点同意Jon Skeet的观点,但这在很大程度上取决于用法 如果您有很多线程调用get或多或少是同时进行的,那么您可能想要一个readlock,而不是同步它 编辑:顺便说一句,在有效的Java第二版中有一个很好的例子。。。
我还必须在这里纠正自己的错误,考虑到上下文,如果以一种智能的方式进行同步,那么同步也会很好地工作,而writelock可能也会在这种上下文中工作。如果您试图让一个单例执行以下操作,它不是更简单吗:
private final ProductCatalog productCatalog = productCatalogLoader.load();
public ProductCatalog get() {
return this.productCatalog;
}
如果由于某种原因无法使ProductCatalog成为最终成员,那么使用synchronized per Jon Skeet的答案或原始代码应该可以工作,只要成员ProductCatalog未被访问,除非通过get调用或重新加载 如果你想让单身汉做以下事情,不是更简单吗
private final ProductCatalog productCatalog = productCatalogLoader.load();
public ProductCatalog get() {
return this.productCatalog;
}
如果由于某种原因无法使ProductCatalog成为最终成员,那么使用synchronized per Jon Skeet的答案或原始代码应该可以工作,只要成员ProductCatalog未被访问,除非通过get调用或重新加载 这看起来像。问题在于,可以在完全构造对象之前指定productCatalog
说我们有
var foo = new ProductCatalog();
foo.blah = "blah blah";
this.productCatalog = foo;
可以按以下方式重新排序
var foo = new ProductCatalog();
this.productCatalog = foo;
foo.blah = "blah blah";
这看起来像。问题在于,可以在完全构造对象之前指定productCatalog
说我们有
var foo = new ProductCatalog();
foo.blah = "blah blah";
this.productCatalog = foo;
可以按以下方式重新排序
var foo = new ProductCatalog();
this.productCatalog = foo;
foo.blah = "blah blah";
嗯,我倾向于同意。但是使用不带r的写锁安全吗
艾德洛克?可以说更复杂,是的,我倾向于同意。但是,在没有读锁的情况下使用写锁安全吗?可以说,更复杂的是,关键短语是不安全的发布。关键短语是不安全的发布。