Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 这个类是线程安全的延迟加载,还是像“;双重检查锁定“;?_Java_Thread Safety - Fatal编程技术网

Java 这个类是线程安全的延迟加载,还是像“;双重检查锁定“;?

Java 这个类是线程安全的延迟加载,还是像“;双重检查锁定“;?,java,thread-safety,Java,Thread Safety,示例代码如下所示 公共类{ 公共服务(供应商){ this.supplier=Objects.requirennull(供应商); } 供应商; T值; @凌驾 公共部门得不到{ 如果(供应商!=空){ 已同步(此){ 如果(供应商!=空){ value=supplier.get(); 供应商=空; } } } 返回值; } } 我担心如果“供应商”是一个建造商。“supplier=null”可以在对象初始化之前执行。可能会发生类似于“双重检查锁定已断开”的错误 “supplier.get()

示例代码如下所示

公共类{
公共服务(供应商){
this.supplier=Objects.requirennull(供应商);
}
供应商;
T值;
@凌驾
公共部门得不到{
如果(供应商!=空){
已同步(此){
如果(供应商!=空){
value=supplier.get();
供应商=空;
}
}
}
返回值;
}
}
我担心如果“供应商”是一个建造商。“supplier=null”可以在对象初始化之前执行。可能会发生类似于“双重检查锁定已断开”的错误

“supplier.get()==null”在此类中可能为true。所以我不检查值是否为null

如果线程不安全,我是否应该在“供应商”字段前添加“volatile”?
如果它是线程安全的,为什么呢?

理论上,问题是您无法控制哪个supplier.get()将返回。它可以是null,每次都可以是不同的值,等等。出于这个原因,我声明这段代码不是线程安全的。还要注意,“供应商”只有在满足以下条件时才会为空:

new Lazy(null)
否则它将永远不会为空。在这种情况下,您还可以在构造函数中抛出一个异常

    public Lazy(Supplier<T> supplier) {
        if (supplier == null) {
            throw new IllegalArgumentException("'supplier' must not be null");
        }
        this.supplier = supplier;
    }

这有点复杂,但请阅读。简言之,没有挥发性,这是打破。整个结构可以大大简化:

public class Lazy<T> implements Supplier<T> {
    
     private final Supplier<T> supplier;

     volatile boolean computed = false;

     T value;

     public Lazy(Supplier<T> supplier) {
          this.supplier = Objects.requireNonNull(supplier);
     }

     @Override
     public T get() {
          if (!computed) {
                synchronized (this) {
                   if (!computed) {
                        value = supplier.get();
                        computed = true;
                   }
                }
           }

        return value;
     }

}
公共类{
私人最终供应商;
可变布尔值=假;
T值;
公共服务(供应商){
this.supplier=Objects.requirennull(供应商);
}
@凌驾
公共部门得不到{
如果(!计算){
已同步(此){
如果(!计算){
value=supplier.get();
计算=真;
}
}
}
返回值;
}
}

使用另一个变量而不是设置supplier=null不是更好吗?
get
是一个实例方法,因此调用它的唯一方法是通过
Lazy
的实例,因此您需要调用
Lazy
的构造函数,因此为什么不将
supplier
设置为final并在(供应商!=空){check?为什么要同步
get
的结果,而不是
供应商本身呢?否则
T
一定是易变的,是的。谢谢你提醒我。我写了错误的示例代码。我已经更正了内容谢谢你提醒我。我写了错误的示例代码。我已经更正了内容OK,No在新代码中,supplier在方法get()内永远不会为null,因为方法get()在构造函数完成之前永远不会执行。这是因为为了使线程能够使用“lazySupplier.get()”,首先它必须将完全构造的“lazySupplier”实例分配到引用“lazySupplier”中只有这样,它才能调用“lazySupplier.get()”。这意味着,构造函数必须在调用实例中的任何方法之前完成。@user10339780发生这种情况时,由您在代码中做出正确的反应。“将不起作用”具体是什么意思?可能是你想在发生这种情况时抛出异常?我无法读取Mind我希望在第一次调用Supplier后不需要重复计算缓存结果。如果“Supplier.get()==null”,Supplier.get将在您的代码中被重复调用。@user10339780正确。那么您想说您也想缓存一个
null
值吗?这意味着
null
存储在
value
中并且只计算一次是非常好的。这将是一个相当奇怪的不变量。我只是希望我可以简单地使用“lazy Supplier”无需判断返回值是否为null@user10339780我想这就是你故意引入供应商空检查的原因。更好的方法是引入另一个变量,请参见编辑。但我真的不知道这会给调用者带来什么,他们现在如何有意义地使用
null
public class Lazy<T> implements Supplier<T> {
    
     private final Supplier<T> supplier;

     volatile boolean computed = false;

     T value;

     public Lazy(Supplier<T> supplier) {
          this.supplier = Objects.requireNonNull(supplier);
     }

     @Override
     public T get() {
          if (!computed) {
                synchronized (this) {
                   if (!computed) {
                        value = supplier.get();
                        computed = true;
                   }
                }
           }

        return value;
     }

}