java lazy initialization holder_java – 没有volatile的懒惰初始化/ memoization

看起来Java内存模型没有定义本地缓存的“刷新”和“刷新”,相反人们只是为了简单而这样称呼它,但实际上“发生在之前”的关系意味着以某种方式刷新和刷新(如果你可以解释一下,但不是问题的直接部分).

这让我非常困惑,因为关于Java Memory Model in the JLS的部分不是以易于理解的方式编写的.

因此,请您告诉我,我在以下代码中做出的假设是否正确,是否保证正确运行?

它部分基于Double-checked locking维基百科文章中提供的代码,但作者使用了包装类(FinalWrapper),但其原因并不完全明显.也许支持空值?

public class Memoized {

private T value;

private volatile boolean _volatile;

private final Supplier supplier;

public Memoized(Supplier supplier) {

this.supplier = supplier;

}

public T get() {

/* Apparently have to use local variable here, otherwise return might use older value

* see https://jeremymanson.blogspot.com/2008/12/benign-data-races-in-java.html

*/

T tempValue = value;

if (tempValue == null) {

// Refresh

if (_volatile);

tempValue = value;

if (tempValue == null) {

// Entering refreshes, or have to use `if (_volatile)` again?

synchronized (this) {

tempValue = value;

if (tempValue == null) {

value = tempValue = supplier.get();

}

/*

* Exit should flush changes

* "Flushing" does not actually exists, maybe have to use

* `_volatile = true` instead to establish happens-before?

*/

}

}

}

return tempValue;

}

}

我还读过构造函数调用可以内联并重新排序,从而导致对未初始化对象的引用(参见this comment on a blog).直接分配供应商的结果是否安全,或者必须分两步完成?

value = tempValue = supplier.get();

两个步骤:

tempValue = supplier.get();

// Reorder barrier, maybe not needed?

if (_volatile);

value = tempValue;

编辑:这个问题的标题有点误导,目标是减少挥发性字段的使用.如果初始化值已经在线程的高速缓存中,则直接访问值而无需再次查看主存储器.

解决方法:

如果你只有几个单身,你可以减少挥发性的使用.注意:您必须为每个单例重复此代码.

enum LazyX {

;

static volatile Supplier xSupplier; // set somewhere before use

static class Holder {

static final X x = xSupplier.get();

}

public static X get() {

return Holder.x;

}

}

如果您了解供应商,这将变得更加简单

enum LazyXpensive {

;

// called only once in a thread safe manner

static final Xpensive x = new Xpensive();

// after class initialisation, this is a non volatile read

public static Xpensive get() {

return x;

}

}

您可以使用Unsafe避免使字段变得不稳定

import sun.misc.Unsafe;

import java.lang.reflect.Field;

import java.util.function.Supplier;

public class LazyHolder {

static final Unsafe unsafe = getUnsafe();

static final long valueOffset = getValueOffset();

Supplier supplier;

T value;

public T get() {

T value = this.value;

if (value != null) return value;

return getOrCreate();

}

private T getOrCreate() {

T value;

value = (T) unsafe.getObjectVolatile(this, valueOffset);

if (value != null) return value;

synchronized (this) {

value = this.value;

if (value != null) return value;

this.value = supplier.get();

supplier = null;

return this.value;

}

}

public static Unsafe getUnsafe() {

try {

Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");

theUnsafe.setAccessible(true);

return (Unsafe) theUnsafe.get(null);

} catch (NoSuchFieldException | IllegalAccessException e) {

throw new AssertionError(e);

}

}

private static long getValueOffset() {

try {

return unsafe.objectFieldOffset(LazyHolder.class.getDeclaredField("value"));

} catch (NoSuchFieldException e) {

throw new AssertionError(e);

}

}

}

但是,额外查找是微优化.如果您愿意为每个线程执行一次同步命中,则可以完全避免使用volatile.

标签:java,memoization,lazy-initialization,java-memory-model

来源: https://codeday.me/bug/20190622/1261366.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值