Java线程安全实现懒初始化两个方式

15-01-30 banq
Java中以线程安全方式实现懒初始化对象有很多写法,这里不讨论针对全局单例场景,而是讨论缓存的使用场景,通常为了提高性能,我们经常通过key/对象形式将对象保存到内存中,具体来说,首先我们需要检查缓存中或application作用域中是否有存在对应某个key的对象,如果不存在我们new一个对象实例放进去,这个过程是懒初始化过程,多线程环境为防止并发同时初始化对象实例 ,大概有两种方式:

使用同步方式:
来自:http://www.codeproject.com/Articles/418425/Demystifying-concurrent-lazy-load-pattern

public IList<Product> GetProducts()
{ 
    // declare local variable for cache access
    // and use it to be safe on sudden cache resetting 
    List<Product> result = this.localCache;
    if (result  == null) 
    {
        lock (this.syncLock)
        {
            // recheck condition after acquiring lock
            // to be sure cache is not loaded while waiting for the lock
            result = this.localCache;
            if (result  == null)
            { 
                // it is important to first store result in local variable 
                // and only after in cache because in between cache 
                // might be reset 
                result = this.LoadProductsFromDatabase(); 
                this.localCache = result; 
            } 
        } 
    }
 
    // important – return local variable and not this.localCache
    // above code guaranties that result will have value
    // on the other hand this.localCache might be reset at any time 
    return result; 
}
 
public void Reset() 
{ 
    // no need for any kind of locking 
    // because our lazy load can handle resetting at any time 
    this.localCache = null; 
}   
<p class="indent">




非同步的方式:
来自:http://timezra.blogspot.com/2008/04/threadsafe-lazy-instantiation-without.html

class ThreadsafeLazyGetter extends LazyGetter {
  private final AtomicReference<Object> lazilyCreatedObject = 
    new AtomicReference<Object>();
  
  @Override
  Object get() {
    final Object existingValue = lazilyCreatedObject.get();
    if (existingValue != null) {
      return existingValue;
    }
    final Object newValue = performLongRunningInitialization();
    if (lazilyCreatedObject.compareAndSet(null, newValue)) {
      return newValue;
    }
    return lazilyCreatedObject.get();
  }
}
<p class="indent">


点评:虽然非同步方式从理论上看没有锁,应该比同步方式快,但是代码不是很直接,相当于加了个套子AtomicReference,这两者还是各有其使用场景的。

Java 8引入了Lambda,见:用Java 8实现懒初始化,有些类似上面非同步方式,代码比较重量,关键是Java不是将函数作为第一等公民导致,看看Javascript中实现:

var tasksPromise; 
function getTasks() { 
  taskPromise = taskPromise || getTasksFromTheServer();
  return taskPromise;
}
<p class="indent">

上述代码中getTasksFromTheServer只会执行一次。来自:NodeJS的Promise的用法

[该贴被banq于2015-01-30 21:40修改过]