// 單線程的時候 class Foo { private Helper helper = null; public Helper getHelper() { if (helper == null) { helper = new Helper(); } return helper; } }
這段在使用多線程的情況下無法正常工作。在多個線程同時調用getHelper()
時,必須要獲取鎖,否則,這些線程可能同時去創建對象,或者某個線程會得到一個未完全初始化的對象。
鎖可以通過代價很高的同步來獲得,就像下面的例子一樣。
//這樣寫雖然正確,但是粗暴地把getHelper鎖住了,這樣代價很大 class Foo { private Helper helper = null; public synchronized Helper getHelper() { if (helper == null) { helper = new Helper(); } return helper; } }
只有getHelper()
的第一次調用需要同步創建對象,創建之后getHelper()
只是簡單的返回成員變量,而這里是無需同步的。 由於同步一個方法會降低100倍或更高的性能[2], 每次調用獲取和釋放鎖的開銷似乎是可以避免的:一旦初始化完成,獲取和釋放鎖就顯得很不必要。許多程序員一下面這種方式進行優化:
- 檢查變量是否被初始化(不去獲得鎖),如果已被初始化立即返回這個變量。
- 獲取鎖
- 第二次檢查變量是否已經被初始化:如果其他線程曾獲取過鎖,那么變量已被初始化,返回初始化的變量。
- 否則,初始化並返回變量。
//這種雙重檢查鎖定就很好地解決問題,避免每次調用都要獲取鎖 class Foo { private Helper helper = null; public Helper getHelper() { if (helper == null) { synchronized(this) { if (helper == null) { helper = new Helper(); } } } return helper; } }