單例設計模式就是要保證單個類的對象在內存中的唯一性。外部不能new單例設計的 類對象,所以必須私有其構造方法。
- 保證唯一性,構造方法只能私有
- 構造私有,外部無法建立對象,所以只能內部new對象
- 為了拿到對象,接著只能建立靜態(tài)方法(外部沒有辦法new對象,所以只能類名調用靜態(tài)方法),返回內部對象
- 又因為靜態(tài)不能訪問非靜態(tài),所以內部對象也只能是靜態(tài)的
單例設計模式也叫餓漢,除此之外還有一種叫懶漢(內部對象的延遲加載,線程不安全),詳情見代碼
demo:
package single;
/*
* 單利設計模式,保證對象在內存中的唯一性
*
* 1.保證唯一性,構造方法只能私有
* 2.構造私有,外部無法建立對象
* 3.只能內部new對象出來
* 4.為了拿到對象,接著只能建立靜態(tài)方法,返回內部對象
* 5.靜態(tài)不能訪問非靜態(tài),所以內部對象也只能是靜態(tài)的
*
* 懶漢式線程不安全
*
* java內的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內也有單例設計模式的類對象: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();
/*
* 結果出現(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);
}
}
}