首页 文章

无锁和无等待的线程安全延迟初始化

提问于
浏览
27

要执行无锁和无等待的延迟初始化,请执行以下操作:

private AtomicReference<Foo> instance = new AtomicReference<>(null);  

public Foo getInstance() {
   Foo foo = instance.get();
   if (foo == null) {
       foo = new Foo();                       // create and initialize actual instance
       if (instance.compareAndSet(null, foo)) // CAS succeeded
           return foo;
       else                                   // CAS failed: other thread set an object 
           return instance.get();             
   } else {
       return foo;
   }
}

除了一件事之外,它的效果非常好:如果两个线程看到实例 null ,它们都会创建一个新对象,只有一个幸运的是通过CAS操作来设置它,这会浪费资源 .

有没有人建议另一个 lock-free 懒惰的初始化模式,这会降低两个并发线程创建两个昂贵对象的可能性?

5 回答

  • 0

    如果你想要真正的锁定自由,你将不得不做一些旋转 . 您可以拥有一个线程'获胜'创建权,但其他人必须旋转直到它准备就绪 .

    private AtomicBoolean canWrite = new AtomicBoolean(false);  
    private volatile Foo foo; 
    public Foo getInstance() {
       while (foo == null) {
           if(canWrite.compareAndSet(false, true)){
               foo = new Foo();
           }
       }
       return foo;
    }
    

    这显然存在繁忙旋转的问题(你可以在那里放一个睡眠或产量),但我可能仍然会推荐Initialization on demand .

  • 3

    我认为你需要为对象创建本身进行一些同步 . 我会做:

    // The atomic reference itself must be final!
    private final AtomicReference<Foo> instance = new AtomicReference<>(null);
    public Foo getInstance() {
      Foo foo = instance.get();
      if (foo == null) {
        synchronized(instance) {
          // You need to double check here
          // in case another thread initialized foo
          Foo foo = instance.get();
          if (foo == null) {
            foo = new Foo(); // actual initialization
            instance.set(foo);
          }
        }
      }
      return foo;
    }
    

    这是一种非常常见的模式,特别是对于懒惰的单身人士 . Double checked locking最小化 synchronized 块实际执行的次数 .

  • 22

    我可能会使用lazy init Singleton模式:

    private Foo() {/* Do your heavy stuff */}
    
    private static class CONTAINER {
     private static final Foo INSTANCE = new Foo();
    }
    
    public static Foo getInstance() {
     return CONTAINER.INSTANCE;
    }
    

    我实际上没有看到任何使用AtomicReference成员字段的原因 .

  • 0

    如何使用另一个 volatile 变量锁定?你可以使用新变量进行双锁吗?

  • -1

    我不确定最终结果是否应该以性能为中心,如果下面的是,则不是解决方案 . 请你检查两次例如,在第一次检查后调用thread.sleep方法,随机mili秒小于100毫秒 .

    private AtomicBoolean canWrite = new AtomicBoolean(false);  
    private volatile Foo foo; 
    public Foo getInstance() {
       if(foo==null){
              Thread.Sleep(getRandomLong(50)) // you need to write method for it
             if(foo==null){
                foo = new Foo();
          }
       }
       return foo;
    }
    

相关问题