java單例模式
- 懶漢式單例
- 餓漢式單例
- 登記式單例
單例模式的特點
- 單例類只能有一個實例
- 單例類必須自己創建自己的唯一實例
- 單例類必須給所有其他對象提供這一實例
線程安全的概念
幾種單例模式的實現與比較(懶漢,餓漢,登記)
1.懶漢式單例,先上代碼(只適用於單線程,不好)
/**
*
* <p>description:懶漢,線程不安全</p>
* @author AbnerLi
* @date 2017年10月12日上午10:56:50
*/
public class LazySingleton {
private static LazySingleton lazySingleton = null;
/**
* 這裏的構造方法設計成爲private 就是防止外部對這種單例進行實例化。(其實也能進行實例化,就是通過java反射機制,可以修改訪問權限)
*/
private LazySingleton(){
}
/**
* <p>description:獲取實例的方法</p>
* @author AbnerLi
* @date 2017年10月12日上午11:33:26
* @return
*/
public static LazySingleton getInstance(){
if(lazySingleton == null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
public static synchronized LazySingleton getInstance(){
if(lazySingleton == null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
public static LazySingleton getInstance(){
if(lazySingleton == null){//如果爲空纔去獲取同步鎖
synchronized (LazySingleton.class) {
if(lazySingleton == null){
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}
上面這種方式優化了懶漢式單例,只有當lazySingleton爲null的時候採取獲取同步鎖,創建一次實例。當實例被創建,後面就不需要試圖加鎖了。但是上面獲取實例都需要進行加鎖的,有沒有不需要加鎖的呢?當然有 /**
* 靜態內部類,效率最高,不用同步
*/
private static class LazyHolder{
private static final LazySingleton instance = new LazySingleton();
}
public static final LazySingleton getStaticClassLazySingleton(){
return LazyHolder.instance;
}
這種方式比上面所有方式好一些,即實現了線程安全,同時又避免了同步帶來的性能影響。2.餓漢式單例(建議使用)
/**
*
* <p>description:餓漢式單例類,在類的初始化時就自行實例化了,因爲java的靜態變量,在類的加載的時候實例化</p>
* @author AbnerLi
* @date 2017年10月13日上午9:44:35
*/
public class HungrySingleton {
//私有的構造方法
private HungrySingleton(){
}
//自行實例化
private static final HungrySingleton instance = new HungrySingleton();
//靜態工廠方法
public static HungrySingleton getInstance(){
return instance;
}
}
餓漢式單例,採用的式靜態屬性,因爲在java中類中的靜態成員會隨着類的加載而加載。因此可以直接調用HungrySingleton3.登記式單例(建議使用)
/**
*
* <p>description:登記式單例,類似於Spring裏面的方法,將類名註冊,下次從裏面直接獲取</p>
* @author AbnerLi
* @date 2017年10月13日上午10:28:53
*/
public class SignInSingleton {
private static Map<String, SignInSingleton> map = new HashMap<String, SignInSingleton>();
/**
* 靜態塊,將實例放入map中
*/
static{
SignInSingleton instance = new SignInSingleton();
map.put(instance.getClass().getName(), instance);
}
//保護的默認構造方法
protected SignInSingleton() {
// TODO Auto-generated constructor stub
}
//靜態工廠方法,返還此類唯一的實例
public static SignInSingleton getInstance(String name){
//獲取完整類名
if(name == null){
name = SignInSingleton.class.getName();
System.out.println("name == null" + "---->name=" + name);
}
//map中沒有實例,就創建一個
if(map.get(name) == null){
try {
map.put(name, (SignInSingleton)Class.forName(name).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
return map.get(name);
}
//一個示意性的方法
public String about(){
return "Hello , I am RegSingleton.";
}
public static void main(String [] args){
SignInSingleton signInSingleton = SignInSingleton.getInstance(null);
System.out.println(signInSingleton.about());
}
}
登記式單例事實上式維護了一組單例類的實例,將這些實例存放在一個Map(登記薄)中,對於已經登記過的實例,則從Map直接返回,對於沒有等級的,先登記然後再返回。