简体   繁体   English

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

[英]Lock-free and wait-free thread-safe lazy initialization

To perform lock-free and wait-free lazy initialization I do the following: 要执行无锁和无等待的延迟初始化,请执行以下操作:

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;
   }
}

and it works pretty well except for one thing: if two threads see instance null , they both create a new object, and only one is lucky to set it by CAS operation, which leads to waste of resources. 除了一件事之外,它的效果非常好:如果两个线程看到实例为null ,它们都会创建一个新对象,只有一个幸运的是通过CAS操作来设置它,这会导致浪费资源。

Does anyone suggest another lock-free lazy initialization pattern, which decrease probability of creating two expensive objects by two concurrent threads? 有没有人建议另一种无延迟初始化模式,这会降低两个并发线程创建两个昂贵对象的可能性?

If you want true lock-freedom you will have to do some spinning. 如果你想要真正的锁定自由,你将不得不做一些旋转。 You can have one thread 'win' creation rights but the others must spin until it's ready. 您可以拥有一个线程'获胜'创建权,但其他人必须旋转直到它准备就绪。

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;
}

This obviously has its problems of busy spinning (you can put a sleep or yield in there), but I would probably still recommend Initialization on demand . 这显然存在繁忙旋转的问题(你可以在那里放一个睡眠或产量),但我可能仍然会建议按需初始化

I think you need to have some synchronization for the object creation itself. 我认为你需要为对象创建本身进行一些同步。 I would do: 我会做:

// 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;
}

This is a very common pattern especially for lazy singletons. 这是一种非常常见的模式,特别是对于懒惰的单身人士。 Double checked locking minimizes the number of times the synchronized block is actually executed. 双重检查锁定可最大限度地减少实际执行synchronized块的次数。

I would probably go with the lazy init Singleton pattern: 我可能会使用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;
}

I do not actually see any reason on using an AtomicReference member field for itself. 我实际上没有看到任何使用AtomicReference成员字段的原因。

What about using another volatile variable to lock? 如何使用另一个volatile变量来锁定? You can do the double lock with new variable? 你可以使用新变量进行双锁吗?

I am not sure if end result should be performance centric or not , if yes below is not solution . 我不确定最终结果是否应该以性能为中心,如果下面的是,则不是解决方案。 can you please check twice for instance and after first check call thread.sleep method for random mili seconds less than 100 mili seconds. 请你检查两次例如,在第一次检查后调用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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM