終於把簡直offer看完了一遍
所以第二遍我決定要美一個題自己去實現一遍,會加入自己的理解(但是不一定對哈)
題目:設計一個類,我們只能生成該類的一個實例。
餓漢試
package T2Singleton;
/**
* 餓漢式
* @author yxx
*
*/
public class Singleton {
//私有構造方法
private Singleton() {}
private static Singleton singleton = new Singleton();
public static Singleton getInstance() {
return singleton;
}
}
我第一下想到的就是餓漢試的單例模式,因爲他可以在多線程下使用,不想一般的懶漢式那樣。
餓漢式在類創建的同時就已經創建好一個靜態的對象供系統使用,以後不再改變,所以是線程安全的。
如果是懶漢式,我們就得加同步鎖了
懶漢式
考慮同步,但是是對方法加鎖
package T2Singleton;
/**
* 多線程
*
* @author yxx
*
*/
public class Singleton2 {
//私有構造方法
private Singleton2() {}
private static Singleton2 instance = null;
public synchronized static Singleton2 getInstance() {
if (instance == null) {
instance = new Singleton2();
}
return instance;
}
}
考慮同步,對對象上鎖,使鎖的粒度變小
package T2Singleton;
/**
* 多線程
*
* @author yxx
*
*/
public class Singleton2 {
// 私有構造方法
private Singleton2() {}
private static Singleton2 instance = null;
public static Singleton2 getInstance() {
if (instance == null) {
synchronized (instance) {
if (instance == null) {
instance = new Singleton2();
}
}
}
return instance;
}
}
上面這種,加同步鎖前後兩次判斷實例是否存在
加鎖耗時。可以實現只有當single爲null即沒有創建時,需要加鎖操作,當single創建出來之後,則無須加鎖。
還有一種登記式的,有興趣可以自行研究一下
package T2Singleton;
/**
* 按需創建
*
* @author yxx
*
*/
public class Singleton3 {
Singleton3() {}
public static Singleton3 getInstance() {
return Nested.instance;
}
static class Nested {
Nested() {}
final static Singleton3 instance = new Singleton3();
}
}
如果當我們第一次試圖通過屬性Single3.instance得到Single3的實例時,會自動調用Nested的靜態構造函數創建實例instance。如果我們不調用屬性,那麼就不會觸發運行,也不會創建實例。
餓漢式和懶漢式區別
這兩種乍看上去非常相似,其實是有區別的,主要兩點
1、線程安全:
餓漢式是線程安全的,可以直接用於多線程而不會出現問題,懶漢式就不行,它是線程不安全的,如果用於多線程可能會被實例化多次,失去單例的作用。
如果要把懶漢式用於多線程,有兩種方式保證安全性,一種是在getInstance方法上加同步,另一種是在使用該單例方法前後加雙鎖。
2、資源加載:
餓漢式在類創建的同時就實例化一個靜態對象出來,不管之後會不會使用這個單例,會佔據一定的內存,相應的在調用時速度也會更快,
而懶漢式顧名思義,會延遲加載,在第一次使用該單例的時候纔會實例化對象出來,第一次掉用時要初始化,如果要做的工作比較多,性能上會有些延遲,之後就和餓漢式一樣了。