總結了兩個經典的懶載入單例模式
先上代碼:
[java] view plain copy public class ImageLoader { private volatile static ImageLoader instance; // Returns singleton class instance public static ImageLoader getInstance() { if (instance == null) { synchronized (ImageLoader.class) { if (instance == null) { instance = new ImageLoader(); } } } return instance; } }
這是EventBus中的代碼。
如果直接給方法加上synchronized。所有getInstance()的調用都要同步了。其實我們只是在第一次new對象的時候要同步。而同步需要消耗效能
因此兩次檢查是否instance == null,第一個null是為了避免每次都加鎖,畢竟這有一定的開銷。第二個是為了避免同步問題,比如十個線程同時調用getInstance()方法,都執行了第一步檢查instance == null,並且其中一個線程成功獲得了鎖(執行了synchronized語句),接下來如果沒有再一次判斷instance == null,則十個線程將產生10對象,這違背了單例的初衷。
上述代碼除了有兩次檢查instance == null,另一個特點是變數instance的型別宣告中添加了volatile,因為像下面這種建立對象的語句並不是原子操作,volatile可以使其成為原子操作,避免同步問題。 [java] view plain copy instance = new ImageLoader();
雖說部分JVM沒有完全volatile,但是目前主流的JVM貌似已經都支援了,所以這個問題一般可以忽略。
另外推薦一個更好的簡潔方法:
使用靜態內部類。
public class OssClient { //log private Logger logger = LoggerFactory.getLogger(this.getClass()); //阿里雲API的內或外網網域名稱 private static String END_POINT; //阿里雲API的密鑰Access Key ID private static String ACCESS_KEY_ID; //阿里雲API的密鑰Access Key Secret private static String ACCESS_KEY_SECRET; //阿里雲API的BUCKET_NAME private static String BUCKET_NAME; public static class clientSingletonHolder{ //init on demand static OSSClient client = new OSSClient(END_POINT,ACCESS_KEY_ID, ACCESS_KEY_SECRET); } /** * 擷取阿里雲OSS用戶端對象 * */ public OSSClient getOSSClient(){ return clientSingletonHolder.client; } }
解釋一下,因為java機制規定,內部類clientSingletonHolder只有在getOSSClient()方法第一次調用的時候才會被載入(實現了lazy),而且其載入過程是安全執行緒的(實現安全執行緒)。內部類載入的時候執行個體化一次client 。