大家好,我是野豬。
題目:設計一個類,我們只能生成該類的一個實例。
第一種:只適用於單線程環境
單利模式最簡單的也是不會用的寫法
特徵:只適用於單線程環境
問題:當2個線程同時運行到instance·是否爲null的if語句,並且instance的確沒有創建的時候 那麼2個線程都會創建一個實例。
java代碼:
public class Singleton1 {
private Singleton1() {
}
private static Singleton1 singleton1 = null;
public static Singleton1 getInstance() {
if (singleton1 == null) {
singleton1 = new Singleton1();
}
return singleton1;
}
}
第二種:多線程環境中能工作但是效率不高
雙重加鎖 適合多線程但是因爲加鎖耗時導致效率很低
特徵:加上了同步鎖
問題:由於在一個時刻只有一個線程能拿到同步鎖,當A線程拿到同步鎖的時候,B線程只能等待
當A線程發現沒有創建實例就會去創建實例,代碼走完,然後A線程釋放同步鎖;然後B線程家加上同步鎖,並運行接下來的代碼;
現在存在一個問題就是A線程加上同步鎖並創建完實例後B線程完全沒有必要再加鎖走判斷有沒有實例了。 ps:加鎖是一個非常耗時的操作,在沒有必要的時候應儘量避免。
java代碼:
public class Singleton2 {
private Singleton2() {
}
private static Singleton2 singleton2 = null;
public static Singleton2 getInstance() {
//加上同步鎖
synchronized (Singleton2.class) {
if (singleton2 == null) {
singleton2 = new Singleton2();
}
}
return singleton2;
}
}
第三種:加同步鎖前後兩次判斷實例是否已經存在
特徵:爲了避免A線程獲取到同步鎖後創建了實例,然後釋放鎖;B線程獲取到同步鎖,接着走判斷的是否創建實例的情況;因爲加鎖耗時。
加鎖之前判斷一次有沒有實例;
在首次沒有創建實例的情況下,現在A線程獲取同步鎖創建實例,釋放鎖;B線程線先判斷有無實例,有則不用運行接下來的代碼了。
Java代碼:
public class Singleton3 {
private Singleton3() {
}
private static Singleton3 singleton3 = null;
public static Singleton3 getInstance() {
if (singleton3 == null) {
//加上同步鎖
synchronized (Singleton3.class) {
if (singleton3 == null) {
singleton3 = new Singleton3();
}
}
}
return singleton3;
}
}
雙重加鎖是比較常用的一種寫法,確保了多線程環境下只創建一個實例,並且用兩個if判斷提高效率。
第四種:餓漢式單列
特徵:在類初始化的時候 已經自行實例化;
問題:並不是當你需要才實例化 降低內存的使用率
ps:Java中的靜態代碼塊是在虛擬機加載類的時候,就執行的,而且只執行一次。
非靜態代碼塊是在類new一個實例的時候執行,而且是每次new對象實例都會執行。
java代碼:
public class Singleton4 {
private Singleton4() {
}
private static Singleton4 singleton4 = new Singleton4();
public static Singleton4 getInstance() {
return singleton4;
}
}
第五種:餓漢式單列的變種 靜態代碼塊
特徵:跟第四種是一樣的 都是在類初始化時即實例化instance
java代碼:
public class Singleton5 {
private Singleton5() {
}
private static Singleton5 singleton5 = null;
static {
singleton5 = new Singleton5();
}
public static Singleton5 getInstance() {
return singleton5;
}
}
第六種:靜態內部類
特徵:靜態內部類 改善了3和4的餓漢式 因爲3、4 都是類加載的時候就實例化instance 降低了內存使用效率。只有在真正需要的時候纔會創建實例,提高空間使用效率
因爲內部類Nested{}沒有被主動使用,只有調用getInstance()的時候纔會去實例化instance。
這就避免了類加載的時候就實例化instance
舉例:假設實例化instance很消耗資源,而類中有其他的方法我需要調用,如果類一加載就實例化的話,那太消耗資源了。所以做到真正需要的時候纔去創建實例最好。
java代碼:
public class Singleton6 {
private Singleton6() {
}
public static Singleton6 getInstance() {
return Nested.instance;
}
private static class Nested {
public static Singleton6 instance = new Singleton6();
}
}
以上是自己簡單總結的常見的單例模式的寫法,希望對大家有所幫助。在上一篇中簡單的說了一下單例模式存在的因素和使用的地方,鏈接如下: