Java執行緒安全實現懶初始化兩個方式

banq發表於2015-01-30
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修改過]

相關文章