單例設計模式
單例模式:
定義:某個類在某個系統中只能有一個實體化物件被獲取和使用
實作要點: 1.構造器私有
2.含有一個該類的靜態變數保存這個唯一實體
3.對外提供獲取該實體物件的方式
分類:1.餓漢式 2.懶漢式
下面對這兩種單例模式展開分析,進行對比
餓漢式
1.代碼實作1(靜態常量方式實作)
public class SingleTon{
private static final SingleTon singleTon = new SingleTon();
private SingleTon(){}
public static SingleTon getSingleTon(){
return singleTon;
}
}
2.代碼實作2(靜態代碼塊方式實作)
public class SingleTon{
private staic SingleTon;
//在代碼塊執行時,創建單例物件
static{singleTon = new SingleTon();}
private SingleTon(){}
public static SingleTon getSingleTon(){
return singleTon;}
}`
public class TestSingleTon{
public static void main(Stirng[] args){
SingleTon s1 = SindleTon.getSingleTon;
SingleTon s2 = SindleTon.getSingleTon;
System.out.println(s1 == s2); //true
}
}
3.特點及代碼分析
1)私有化構造器
2)在類的內部創建一個類的實體,static
3)私有化物件,通過公共方法呼叫
4)此方法只能通過類來呼叫,因為是static的,類的實體也是static的
5)只創建一個物件,不會有執行緒不安全的情況
6)在類加載是就完成了類實體化,避免了執行緒同步問題,但沒有達到Lazy Loading的效果,如果沒有使用過這個物件,會造成記憶體浪費
7)如何避免這種情況發生呢?可以采用列舉形式實作單例模式
代碼塊如下:
public enum SingleTon{
INSTANCE;
}`
懶漢式
1.代碼實作1(執行緒不安全方式)
public class SingleTon{
private SingleTon(){}
private static SingleTon singleTon = null;
//當呼叫方法時,才創建單例物件
public static SingleTon getSingleTon(){
if(singleTon == null){
singleTon = new SingleTon();
}
return singleTon;
}
}
2.代碼分析1
1)起到了lazy Loading的作用,即延遲加載物件,但只能在單執行緒時使用
2)如果早多個執行緒下,一個執行緒進入了if(singleTon == null)判斷陳述句時,若滿足條件判斷且還沒來得及繼續執行,另一個執行緒也進入到if(singleTon == null)判斷陳述句,這就會產生多個實體物件,即執行緒不安全,
3.代碼實作2(執行緒安全方式\雙重檢查)
public class SingleTon{
private SingleTon(){}
private static SingleTon singleTon = null;
//當呼叫方法時,才創建單例物件
public static SingleTon getSingleTon(){
if(singleTon == null){//第一層檢查,檢查是否有參考物件,如果一個執行緒獲取了實體,則不需要進入同步代碼塊中了
synchronized (SingleTon.class){//第一層鎖,保證只有一個執行緒進入,同步代碼塊使用的鎖是單例的位元組碼檔案物件,且只能用這個鎖
if(singleTon == null){ //第二層檢查
singleTon = new SingleTon();
}
}
return singleTon;
}
}
4.代碼分析2
//volatile關鍵字的作用為禁止指令重排,保證回傳singleTon物件一定在創建物件后
singleTon = new SingleTon();該陳述句的底層實作邏輯為:
(1)在堆上開辟空間
(2)屬性初始化
(3)參考指向物件
//假設以上三個內容為三條單獨指令,因指令重排可能會導致執行順序為1->3->2(正常為1->2->3),當單例模式中存在普通變數需要在構造方法中進行初始化操作時,單執行緒情況下,順序重排沒有影響;但在多執行緒情況下,假如執行緒1執行singleton=new Singleton()陳述句時先1再3,由于系統調度執行緒2的原因沒來得及執行步驟2,但此時已有參考指向物件也就是singleton!=null,故執行緒2在第一次檢查時不滿足條件直接回傳singleton,此時singleton為null(即str值為null)
//volatile關鍵字可保證singleton=new Singleton()陳述句執行順序為123,因其為非原子性依舊可能存在系統調度問題(即執行步驟時被打斷),但能確保的是只要singleton!=0,就表明一定執行了屬性初始化操作;而若在步驟3之前被打斷,其他執行緒可進入第一層檢查向下執行創建物件.此時執行緒2拿到的不是一個null singleton,而是一個沒有被步驟2正確初始化的singleton,
5.代碼實作3(靜態內部類)
//懶漢式:靜態內部類形式
public class SingleTon {
private SingleTon(){
}
private static class Inner{
private static final SingleTon SINGLE_TON = new SingleTon();
}
public static SingleTon getSingleTon(){
return Inner.SINGLE_TON;
}
}
//分析:(1)只有在呼叫方法時,才會加載到內部類,從而完成類的實體化,singleTon,
(2)避免了執行緒不安全,利用靜態內部類特點實作延遲加載,效率高,
6.實際應用
public class LazySingleDesign {
private static LazySingleDesign lazySingleDesign = null ;
private LazySingleDesign(){}
public static LazySingleDesign getInstance(){
synchronized(LazySingleDesign.class){
if(lazySingleDesign == null){
lazySingleDesign = new LazySingleDesign();
}
}
return lazySingleDesign;
}
}
@Testpublic void test() {ExecutorService executor = Executors.newFixedThreadPool(10);for(int i = 0 ; i < 10;i++){executor.execute(new Runnable() {@Overridepublic void run() {LazySingleDesign user = LazySingleDesign.getInstance();System.out.println("design = " + user);}});}}`
getInstance()方法內的第一個if判斷可以去掉,生成的也是單例,另外可見性可以去掉也不影響生成的單例,
參考文章鏈接:1.https://blog.csdn.net/weixin_42617262/article/details/90448083
2.https://big-data.blog.csdn.net/article/details/83422780?spm=1001.2101.3001.6650.9&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-9-83422780-blog-115265060.pc_relevant_3mothn_strategy_recovery&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-9-83422780-blog-115265060.pc_relevant_3mothn_strategy_recovery&utm_relevant_index=16
3.https://blog.csdn.net/qq_42804736/article/details/115265060
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/545265.html
標籤:Java
上一篇:票據系統設計
下一篇:Java陣列
