搬以前寫的博客【2014-12-30 16:04】
在web應用中服務器面臨的是大量的訪問請求,免不了多線程程序,但是有時候,我們希望在多線程應用中的某一個類只能新建一個對象的時候,就會遇到問題。
首先考慮單線程,如果要求只能新建一個對象,那么構造函數我們要設為private。簡單的想法:
class singleton{ private singleton(){ //..... } private static singleton instance; public static singleton getinstance(){ if(instance==null) //1 instance = new singleton(); //2 return instance } }
這對於單線程是合理的,第一次調用singleton類時,會新建出singleton對象,但之后訪問時,返回的是第一次新建的instance。
但多線程訪問時,有可能同時進入//1處的條件判斷,多次執行2代碼,從而新建多個singleton對象。我們考慮使用同步關鍵字sycronized
class singleton{ private singleton(){ //..... } private static singleton instance; public static singleton synchronized getinstance(){ if(instance==null) //1 instance = new singleton(); //2 return instance } }
此時可以保證不出錯,是單例模式的一種方案,但是問題是每次執行都要用到同步,開銷較大。
另外一種想法是,雙重檢查鎖,代碼如下:
class singleton{ private singleton(){ //..... } private static singleton instance; public static singleton getinstance(){ if(instance==null) { //1 sycronized(singleton.class){ if(instance==null) instance = new singleton(); //2 } } return instance; } }
此寫法保證了,當多個進程進入第一個判斷鎖時,會被同步機制隔離,只有一個程序進入新建對象,再其他線程進入時,instance已經不為null,因此不會新建多個對象。這種方法就叫做雙重檢查鎖,但是也有一個問題,就是java是實行無序寫入的機制,在某個線程執行//2代碼的過程中,instance被賦予了地址,但是singleton對象還沒構造完成時,如果有線程訪問了代碼//1此時判斷instance不為空,但是方法返回的是一個不完整對象的引用。此時可能會產生錯誤!
另外一種實現單例模式的寫法就是
class singleton{ private singleton(){ //..... } private static singleton instance = new singleton(); public static singleton getinstance(){ return instance; } }
這樣就不會有之前的問題了。