国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - 菜鳥學(xué)習(xí)java設(shè)計(jì)模式之單例模式

菜鳥學(xué)習(xí)java設(shè)計(jì)模式之單例模式

2021-02-21 10:46劉水鏡 Java教程

這篇文章主要為大家詳細(xì)介紹了java設(shè)計(jì)模式之單例模式的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下

單例模式大家并不陌生,也都知道它分為什么懶漢式、餓漢式之類的。但是你對單例模式的理解足夠透徹嗎?今天我?guī)Т蠹乙黄饋砜纯次已壑械膯卫赡軙愕恼J(rèn)識有所不同。

下面是一個簡單的小實(shí)例:

java" id="highlighter_148334">
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//簡單懶漢式
public class Singleton {
   
  //單例實(shí)例變量
  private static Singleton instance = null;
   
  //私有化的構(gòu)造方法,保證外部的類不能通過構(gòu)造器來實(shí)例化
  private Singleton() {}
   
  //獲取單例對象實(shí)例
  public static Singleton getInstance() {
     
    if (instance == null) { 
      instance = new Singleton(); 
    }
     
    System.out.println("我是簡單懶漢式單例!");
    return instance;
  }
}

很容易看出,上面這段代碼在多線程的情況下是不安全的,當(dāng)兩個線程進(jìn)入if (instance == null)時,兩個線程都判斷instance為空,接下來就會得到兩個實(shí)例了。這不是我們想要的單例。

接下來我們用加鎖的方式來實(shí)現(xiàn)互斥,從而保證單例的實(shí)現(xiàn)。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//同步法懶漢式
public class Singleton {
   
  //單例實(shí)例變量
  private static Singleton instance = null;
   
  //私有化的構(gòu)造方法,保證外部的類不能通過構(gòu)造器來實(shí)例化
  private Singleton() {}
   
  //獲取單例對象實(shí)例
  public static synchronized Singleton getInstance() {
     
    if (instance == null) { 
      instance = new Singleton(); 
    }
     
    System.out.println("我是同步法懶漢式單例!");
    return instance;
  }
}

加上synchronized后確實(shí)保證了線程安全,但是這樣就是最好的方法嗎?很顯然它不是,因?yàn)檫@樣一來每次調(diào)用getInstance()方法是都會被加鎖,而我們只需要在第一次調(diào)用getInstance()的時候加鎖就可以了。這顯然影響了我們程序的性能。我們繼續(xù)尋找更好的方法。

經(jīng)過分析發(fā)現(xiàn),只需要保證instance = new Singleton()是線程互斥就可以保證線程安全,所以就有了下面這個版本:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//雙重鎖定懶漢式
public class Singleton {
   
  //單例實(shí)例變量
  private static Singleton instance = null;
   
  //私有化的構(gòu)造方法,保證外部的類不能通過構(gòu)造器來實(shí)例化
  private Singleton() {}
   
  //獲取單例對象實(shí)例
  public static Singleton getInstance() {
    if (instance == null) { 
      synchronized (Singleton.class) {
        if (instance == null) { 
          instance = new Singleton(); 
        }
      }
    }
    System.out.println("我是雙重鎖定懶漢式單例!");
    return instance;
  }
}

這次看起來既解決了線程安全問題,又不至于每次調(diào)用getInstance()都會加鎖導(dǎo)致降低性能。看起來是一個完美的解決方案,事實(shí)上是這樣的嗎?

很遺憾,事實(shí)并非我們想的那么完美。java平臺內(nèi)存模型中有一個叫“無序?qū)?rdquo;(out-of-order writes)的機(jī)制。正是這個機(jī)制導(dǎo)致了雙重檢查加鎖方法的失效。這個問題的關(guān)鍵在上面代碼上的第5行:instance = new Singleton(); 這行其實(shí)做了兩個事情:1、調(diào)用構(gòu)造方法,創(chuàng)建了一個實(shí)例。2、把這個實(shí)例賦值給instance這個實(shí)例變量。可問題就是,這兩步j(luò)vm是不保證順序的。也就是說。可能在調(diào)用構(gòu)造方法之前,instance已經(jīng)被設(shè)置為非空了。下面我們一起來分析一下:

假設(shè)有兩個線程A、B

1、線程A進(jìn)入getInstance()方法。
2、因?yàn)榇藭rinstance為空,所以線程A進(jìn)入synchronized塊。
3、線程A執(zhí)行 instance = new Singleton(); 把實(shí)例變量instance設(shè)置成了非空。(注意,是在調(diào)用構(gòu)造方法之前。)
4、線程A退出,線程B進(jìn)入。
5、線程B檢查instance是否為空,此時不為空(第三步的時候被線程A設(shè)置成了非空)。線程B返回instance的引用。(問題出現(xiàn)了,這時instance的引用并不是Singleton的實(shí)例,因?yàn)闆]有調(diào)用構(gòu)造方法。)
6、線程B退出,線程A進(jìn)入。
7、線程A繼續(xù)調(diào)用構(gòu)造方法,完成instance的初始化,再返回。

難道就沒有一個好方法了嗎?好的方法肯定是有的,我們繼續(xù)探索!

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//解決無序?qū)憜栴}懶漢式
public class Singleton {
   
  //單例實(shí)例變量
  private static Singleton instance = null;
   
  //私有化的構(gòu)造方法,保證外部的類不能通過構(gòu)造器來實(shí)例化
  private Singleton() {}
   
  //獲取單例對象實(shí)例
  public static Singleton getInstance() {
    if (instance == null) { 
      synchronized (Singleton.class) {         //1
        Singleton temp = instance;        //2
        if (temp == null) {
          synchronized (Singleton.class) { //3 
            temp = new Singleton();  //4  
          }
          instance = temp;         //5   
        }
      }
    }
    System.out.println("我是解決無序?qū)憫袧h式單例!");
    return instance;
  }  
}

1、線程A進(jìn)入getInstance()方法。
2、因?yàn)閕nstance是空的 ,所以線程A進(jìn)入位置//1的第一個synchronized塊。
3、線程A執(zhí)行位置//2的代碼,把instance賦值給本地變量temp。instance為空,所以temp也為空。
4、因?yàn)閠emp為空,所以線程A進(jìn)入位置//3的第二個synchronized塊。(后來想想這個鎖有點(diǎn)多余)
5、線程A執(zhí)行位置//4的代碼,把temp設(shè)置成非空,但還沒有調(diào)用構(gòu)造方法!(“無序?qū)?rdquo;問題)
6、如果線程A阻塞,線程B進(jìn)入getInstance()方法。
7、因?yàn)閕nstance為空,所以線程B試圖進(jìn)入第一個synchronized塊。但由于線程A已經(jīng)在里面了。所以無法進(jìn)入。線程B阻塞。
8、線程A激活,繼續(xù)執(zhí)行位置//4的代碼。調(diào)用構(gòu)造方法。生成實(shí)例。
9、將temp的實(shí)例引用賦值給instance。退出兩個synchronized塊。返回實(shí)例。
10、線程B激活,進(jìn)入第一個synchronized塊。
11、線程B執(zhí)行位置//2的代碼,把instance實(shí)例賦值給temp本地變量。
12、線程B判斷本地變量temp不為空,所以跳過if塊。返回instance實(shí)例。

到此為止,上面的問題我們是解決了,但是我們突然發(fā)現(xiàn)為了解決線程安全問題,但給人的感覺就像身上纏了很多毛線.... 亂糟糟的,所以我們要精簡一下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//餓漢式
public class Singleton {
   
  //單例變量 ,static的,在類加載時進(jìn)行初始化一次,保證線程安全 
  private static Singleton instance = new Singleton();  
   
  //私有化的構(gòu)造方法,保證外部的類不能通過構(gòu)造器來實(shí)例化。   
  private Singleton() {}
   
  //獲取單例對象實(shí)例   
  public static Singleton getInstance() {
    System.out.println("我是餓漢式單例!");
    return instance;
  }
}

看到上面的代碼,瞬間覺得這個世界清靜了。不過這種方式采用的是餓漢式的方法,就是預(yù)先聲明Singleton對象,這樣帶來的一個缺點(diǎn)就是:如果構(gòu)造的單例很大,構(gòu)造完又遲遲不使用,會導(dǎo)致資源浪費(fèi)。

到底有沒有完美的方法呢?繼續(xù)看:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//內(nèi)部類實(shí)現(xiàn)懶漢式
public class Singleton {
   
  private static class SingletonHolder{
    //單例變量 
    private static Singleton instance = new Singleton();
  }
   
  //私有化的構(gòu)造方法,保證外部的類不能通過構(gòu)造器來實(shí)例化。
  private Singleton() {
     
  }
   
  //獲取單例對象實(shí)例
  public static Singleton getInstance() {
    System.out.println("我是內(nèi)部類單例!");
    return SingletonHolder.instance;
  }
}

懶漢式(避免上面的資源浪費(fèi))、線程安全、代碼簡單。因?yàn)閖ava機(jī)制規(guī)定,內(nèi)部類SingletonHolder只有在getInstance()方法第一次調(diào)用的時候才會被加載(實(shí)現(xiàn)了lazy),而且其加載過程是線程安全的(實(shí)現(xiàn)線程安全)。內(nèi)部類加載的時候?qū)嵗淮蝘nstance。

簡單說一下上面提到的無序?qū)懀@是jvm的特性,比如聲明兩個變量,String a; String b; jvm可能先加載a也可能先加載b。同理,instance = new Singleton();可能在調(diào)用Singleton的構(gòu)造函數(shù)之前就把instance置成了非空。這是很多人會有疑問,說還沒有實(shí)例化出Singleton的一個對象,那么instance怎么就變成非空了呢?它的值現(xiàn)在是什么呢?想了解這個問題就要明白instance = new Singleton();這句話是怎么執(zhí)行的,下面用一段偽代碼向大家解釋一下:

?
1
2
3
4
mem = allocate();       //為Singleton對象分配內(nèi)存。
instance = mem;        //注意現(xiàn)在instance是非空的,但是還沒有被初始化。
 
ctorSingleton(instance);  //調(diào)用Singleton的構(gòu)造函數(shù),傳遞instance.

由此可見當(dāng)一個線程執(zhí)行到instance = mem; 時instance已為非空,如果此時另一個線程進(jìn)入程序判斷instance為非空,那么直接就跳轉(zhuǎn)到return instance;而此時Singleton的構(gòu)造方法還未調(diào)用instance,現(xiàn)在的值為allocate();返回的內(nèi)存對象。所以第二個線程得到的不是Singleton的一個對象,而是一個內(nèi)存對象。

以上就是就是我對單例模式的一點(diǎn)小小的思考跟理解,熱烈歡迎各位大神前來指導(dǎo)批評。

原文鏈接:http://blog.csdn.net/liushuijinger/article/details/9069801

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 色综合网址 | 另类视频在线 | 五月婷婷综合激情网 | 欧美一级毛片日韩一级 | 国产视频一区二区三区在线观看 | 一区二区三区精品 | 成人在线视频观看 | 亚洲日韩中文字幕一区 | 男人的天堂在线视频 | 亚洲精品久久久一区二区三区 | 久久亚 | 国产精品污www一区二区三区 | 国产亚洲一区二区三区 | 在线观看黄色 | www.av在线| 中文字幕日韩在线视频 | 五月在线视频 | 免费视频爱爱太爽了 | 免费国产一区二区 | 欧美成人黄色网 | 久久久在线 | 久久成人精品视频 | 国产精品日本 | 91免费在线视频 | 久久久亚洲| 97久久精品| 校园春色av | 日韩精品专区 | 91在线看 | 午夜爱爱毛片xxxx视频免费看 | 亚洲综合精品久久 | 一级黄毛片 | 天天干天天操 | 中文字幕国产视频 | 久久精品国产一区二区三区不卡 | 日韩有码在线播放 | 欧美日韩久久久久 | 亚洲精品影视 | 久久久精品国产99久久精品芒果 | 国产精品久久久久久久午夜 | 欧美自拍一区 |