單例設計模式就是要保證單個類的對象在內(nèi)存中的唯一性。外部不能new單例設計的 類對象谷羞,所以必須私有其構(gòu)造方法。
- 保證唯一性溜徙,構(gòu)造方法只能私有
- 構(gòu)造私有湃缎,外部無法建立對象,所以只能內(nèi)部new對象
- 為了拿到對象蠢壹,接著只能建立靜態(tài)方法(外部沒有辦法new對象嗓违,所以只能類名調(diào)用靜態(tài)方法),返回內(nèi)部對象
- 又因為靜態(tài)不能訪問非靜態(tài)图贸,所以內(nèi)部對象也只能是靜態(tài)的
單例設計模式也叫餓漢蹂季,除此之外還有一種叫懶漢(內(nèi)部對象的延遲加載,線程不安全)疏日,詳情見代碼
demo:
package single;
/*
* 單利設計模式偿洁,保證對象在內(nèi)存中的唯一性
*
* 1.保證唯一性,構(gòu)造方法只能私有
* 2.構(gòu)造私有沟优,外部無法建立對象
* 3.只能內(nèi)部new對象出來
* 4.為了拿到對象涕滋,接著只能建立靜態(tài)方法,返回內(nèi)部對象
* 5.靜態(tài)不能訪問非靜態(tài)净神,所以內(nèi)部對象也只能是靜態(tài)的
*
* 懶漢式線程不安全
*
* java內(nèi)的Runtime也是單例設計模式
*/
public class SingleTest {
public static void main(String[] args) {
/*
* 單例(餓漢式)
*/
//外部無論如何獲取何吝,都只能拿到一個對象
Single s = Single.getInstance();
System.out.println(s);
Single s1 = Single.getInstance();
System.out.println(s1);
/*
* 單例(懶漢式)
*/
SingleL sL = SingleL.getInstance();
System.out.println(sL);
SingleL sL1 = SingleL.getInstance();
System.out.println(sL1);
}
}
//單例類(餓漢式)
class Single{
private static Single s = new Single();
private Single(){}
public static Single getInstance(){
return s;
}
}
//單例類(懶漢式) 延遲加載 ,線程不安全
class SingleL{
private static SingleL s = null;
private SingleL(){}
public static SingleL getInstance(){
if(s == null)
s = new SingleL();
return s;
}
}
java內(nèi)也有單例設計模式的類對象:Runtime類鹃唯,源碼如下(JAVA 1.8)
public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
...
}
上文提到了懶漢模式(延遲加載)是有線程安全問題的爱榕。下面就解釋為什么在多線程下會出現(xiàn)不安全的情況,怎么來解決這個問題坡慌。
原因:
出現(xiàn)不安全的因素其實很簡單黔酥,因為多線程下會并發(fā)的訪問getInstance方法,有可能一個進程執(zhí)行到if(s == null)
這個邏輯代碼塊中洪橘,此時線程跳走了跪者,但是對象還沒new出來。熄求,另一個進程又加載運行了此方法渣玲。接著原來的那個進程執(zhí)行完剩下的代碼。這樣就new了兩次對象弟晚,導致出現(xiàn)了多個單例對象的實例忘衍,產(chǎn)生了不安全的因素。
那如何來解決呢卿城,具體方法請看下面的示例代碼枚钓。
code:
package thread;
/*
* 懶漢的多線程安全問題解決
*/
public class ThreadDemo3 {
public static void main(String[] args) {
// 建立多線程,來運行
SingleThread s = new SingleThread();
/*
* 結(jié)果出現(xiàn)了兩個懶漢的實例
*
* thread.Single2@6b2ce86d
* thread.Single2@719e4b38
*
* 解決辦法: 將getInstace的方法變?yōu)橥椒椒ㄉ海蛘呒油酱a塊
*/
new Thread(s).start();
new Thread(s).start();
}
}
// 來一個懶漢類
class Single2 {
private static Single2 s = null;
private Single2() {
}
public static Single2 getInstance() {
// 靜態(tài)方法搀捷,鎖上class
// 雙重if,避免不必要的進入同步代碼塊,提高效率
if (s == null) {
synchronized (Single2.class) {
if (s == null)
s = new Single2();
}
}
return s;
}
}
// 來一個Runnable的子類來玩懶漢
class SingleThread implements Runnable {
public void run() {
for (int i = 0; i < 20; i++) {
Single2 s2 = Single2.getInstance();
System.out.println(s2);
}
}
}