在线不卡日本ⅴ一区v二区_精品一区二区中文字幕_天堂v在线视频_亚洲五月天婷婷中文网站

  • <menu id="lky3g"></menu>
  • <style id="lky3g"></style>
    <pre id="lky3g"><tt id="lky3g"></tt></pre>

    常用的設(shè)計模式-單例模式

    這是我的學習筆記,純手打,本想寫在紙質(zhì)筆記本上的,但時間一久就容易丟,所以還是記在網(wǎng)絡(luò)上吧:

    意圖:保證每個類,只有一個實例,并且提供一個全局的訪問點

    場景:需要嚴格的控制全局變量時,如線程對象、數(shù)據(jù)庫連接池對象,不需要多個實例的對象,如工具類。

    雙重檢測加鎖的單例實現(xiàn):

    class LazySingleton{ private volatile static LazySingleton instance; private LazySingleton(){ } public static LazySingleton getInstance(){ if (null == instance){ synchronized(LazySingleton.class){ if(null == instance){ instance = new LazySingleton(); } } } return instance; }}

    這里用了synchronized,先擴展一下筆記synchronized的四種用法

    >>>

    一、synchronized同步鎖為普通對象:

    public void function(){ synchronized (object) { //doSomething… }}

    當某個線程要訪問如上代碼塊中的內(nèi)容時,若該線程獲得object對象的鎖,那么就獲得了執(zhí)行權(quán),否則此線程被阻塞,直到其他線程釋放了object對象的鎖。

    舉個例子:

    public class SyncTest { public static void main(String args[]){ Sync[] syncs = new Sync[5]; for (int i = 0; i < syncs.length; i++) { syncs[i] = new Sync(new Object()); // new了5個新對象到數(shù)組種 } for(Sync sync : syncs){ sync.start(); } }}class Sync extends Thread{ Object syncObj; public Sync(Object syncObj) { this.syncObj = syncObj; } public void run(){ synchronized (syncObj) { System.out.println(Thread.currentThread().getName()+"運行中…"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結(jié)束…"); } }}

    運行結(jié)果:

    Thread-0運行中…Thread-1運行中…Thread-2運行中…Thread-3運行中…Thread-4運行中…Thread-1結(jié)束…Thread-0結(jié)束…Thread-3結(jié)束…Thread-4結(jié)束…Thread-2結(jié)束…

    看到這5個線程并沒有按順序執(zhí)行,他們之間不是同步的。這是因為這5個線程的syncObj并不是指向同一個對象,他們之間不存在同步鎖的競爭,所以是非同步的。將程序改為:

    public class SyncTest { public static void main(String args[]){ Sync[] syncs = new Sync[5]; Object object = new Object(); //改了這里 for (int i = 0; i < syncs.length; i++) { syncs[i] = new Sync(object); //數(shù)組里的對象都是同一個,前面是5個不一樣的 } for(Sync sync : syncs){ sync.start(); } }}class Sync extends Thread{ Object syncObj; public Sync(Object syncObj) { this.syncObj = syncObj; } public void run(){ synchronized (syncObj) { System.out.println(Thread.currentThread().getName()+"運行中…"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結(jié)束…"); } }}

    運行結(jié)果:

    Thread-0運行中…Thread-0結(jié)束…Thread-4運行中…Thread-4結(jié)束…Thread-2運行中…Thread-2結(jié)束…Thread-3運行中…Thread-3結(jié)束…Thread-1運行中…Thread-1結(jié)束…

    這5個線程都達到了效果,但是5個線程的執(zhí)行順序并不是固定的,這是編譯是重排序造成的。

    所以,若想要多個線程同步,則這些線程必須競爭同一個同步鎖。

    二、synchronized同步鎖為類

    類其實也是一個對象,可以去按照普通對象的方式去理解。不同之處在于,普通對象作用于某個實例,而類對象作用于整個類。

    上面的例子我改改:

    public class SyncTest { public static void main(String args[]){ Sync[] syncs = new Sync[5]; for (int i = 0; i < syncs.length; i++) { syncs[i] = new Sync(); } for(Sync sync : syncs){ sync.start(); } }}class Sync extends Thread{ public void run(){ synchronized (SyncTest.class) { // 改了這里 System.out.println(Thread.currentThread().getName()+"運行中…"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結(jié)束…"); } }}

    運行結(jié)果:

    Thread-0運行中…Thread-0結(jié)束…Thread-3運行中…Thread-3結(jié)束…Thread-1運行中…Thread-1結(jié)束…Thread-4運行中…Thread-4結(jié)束…Thread-2運行中…Thread-2結(jié)束…

    這些線程同樣實現(xiàn)了同步,因為他們的同步鎖是同一個對象SyncTest類對象。

    需要注意的是,類對象鎖和普通對象鎖是不同的兩個鎖(即使這個對象是這個類的實例),他們之間互不干擾。

    public class SyncTest { public static void main(String args[]){ Sync[] syncs = new Sync[5]; for (int i = 0; i < syncs.length; i++) { syncs[i] = new Sync(); } Sync1 sync1 = new Sync1(new SyncTest()); for(Sync sync : syncs){ sync.start(); //類對象鎖跑5個線程 } sync1.start(); //普通對象鎖跑一個線程 }}class Sync extends Thread{ public void run(){ synchronized (SyncTest.class) { //類對象鎖 System.out.println(Thread.currentThread().getName()+"運行中…"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結(jié)束…"); } }} class Sync1 extends Thread{ SyncTest syncTest; public Sync1(SyncTest syncTest){ this.syncTest = syncTest; } public void run(){ synchronized (syncTest) { //普通對象鎖 System.out.println(Thread.currentThread().getName()+"運行中…"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結(jié)束…"); } }}

    運行結(jié)果:

    Thread-0運行中…Thread-5運行中…Thread-0結(jié)束…Thread-5結(jié)束…Thread-4運行中…Thread-4結(jié)束…Thread-2運行中…Thread-2結(jié)束…Thread-3運行中…Thread-3結(jié)束…Thread-1運行中…Thread-1結(jié)束…

    可以看到,雖然Sync1中的對象鎖是SyncTest的實例,但是Sync1與Sync的run方法中的synchronized代碼塊并沒有實現(xiàn)同步,他們可以同時訪問這段代碼。驗證了互相不干擾。

    三、synchronized修飾普通方法

    synchronized修飾方法在本質(zhì)上和修飾代碼塊是一樣的,他們都是通過同步鎖來實現(xiàn)同步的。

    public synchronized void syncFunction(){ //doSomething…}

    synchronized修飾普通方法中的同步鎖就是這個對象本身,即 this

    看代碼:

    class Sync extends Thread{ public synchronized void syncFunction(){ doSomething(); } //syncFunction() 和 syncFunction2() 實現(xiàn)的同步效果是一樣的。 public void syncFunction2(){ synchronized (this) { doSomething(); } } private void doSomething(){ //doSomething… }}

    當類中某個方法test()被synchronized關(guān)鍵字所修飾時,所有不同的線程訪問這個類的同一個實例的test()方法都會實現(xiàn)同步的效果。不同的實例之間不存在同步鎖的競爭,也就是說,不同的線程訪問這個類不同實例的test()方法并不會實現(xiàn)同步。這很容易理解,因為不同的實例同步鎖不同,每個實例都有自己的”this”。

    四、synchronized修飾靜態(tài)方法

    public static synchronized void syncFunction(){ //doSomething…}

    同樣的,synchronized作用于靜態(tài)方法時,跟使用類對象作為靜態(tài)鎖的效果是一樣的,此時的類對象就是靜態(tài)方法所屬的類。

    不同的線程訪問某個類不同實例的syncFunction()方法(被synchronized修飾的靜態(tài)方法,如上)時,他們之間實現(xiàn)了同步效果。結(jié)合上面的解釋,這種情況也很好理解:此時不同線程競爭同一把同步鎖,這就是這個類的類對象鎖。

    總結(jié)

    理解synchronized的關(guān)鍵就在于:若想要多個線程同步,則這些線程必須競爭同一個同步鎖。這個同步鎖,可以理解為一個對象。

    >>>

    擴展完畢,繼續(xù)說單例模式

    我們知道,創(chuàng)建一個對象大致分為三步:

    step1 開辟空間

    step2 初始化空間

    step3 賦值

    但是編譯器、即時編譯,CPU可能對字節(jié)碼進行優(yōu)化,對編譯后的字節(jié)碼進行指令的重新排序。比如將第2步和第3步置換位置、那就會出現(xiàn)有一個狀況:

    線程A賦值完畢,但未完成初始化時,線程B在走第一個IF檢查時,發(fā)現(xiàn)instance實例不為null,直接return未初始化的對象,出現(xiàn)異常。

    加入volatile關(guān)鍵字給實例變量,是為了防止JVM進行指令重排而出現(xiàn)的問題。

    private volatile static LazySingleton instance;

    上述時懶漢式實現(xiàn)單例,意為在需要時才去實例化。

    那么對應(yīng)的有餓漢式:

    class HungrySingleton{ //靜態(tài)屬性public static String name=”JJ”;private static HungrySingleton instance = new HungrySingleton(); //構(gòu)造私有化private HungrySingleton(){ } //獲取實例的方法public static HungrySingleton getInstance(){ return instance; }}

    只有在主動使用對應(yīng)的類時,才會觸發(fā)實例的初始化,如當前類是啟動類即main函數(shù)所在的類,直接進行new操作,訪問靜態(tài)屬性、訪問靜態(tài)方法、用反射訪問、初始化一個類的子類等,都會觸發(fā)實例的初始化操作。

    加載的初始化階段就完成了實例的初始化。本質(zhì)上是借助了JVM類加載機制,保證實例的唯一性(初始化過程只會執(zhí)行一次)和線程安全(JVM以同步的形式來完成類加載的整個過程)。

    類加載過程:

    1.加載二進制數(shù)據(jù)到內(nèi)存中,生成對應(yīng)的Class數(shù)據(jù)結(jié)構(gòu)

    2.連接:驗證是否符合規(guī)范,準備給類的靜態(tài)成員變量賦默認值 ,解析

    3.初始化:給類的靜態(tài)變量賦初值

    JDK中的Runtime類就是使用了餓漢式。

    如果通過反射來獲取實例對象呢?是否和getInstance()獲取的實例對象是同一個?

    答案:不是

    反射獲?。?/p>

    //通過構(gòu)造函數(shù)實現(xiàn)Constructor declaredConstructor = HungrySingleton.class.getDeclaredConstractor();//訪問控制權(quán)設(shè)置為truedeclaredConstructor.setAccessible( true );//通過反射實例化HungrySingleton hungrySingleton = declaredConstructor.new Instance();//通過餓漢式實例化HungrySingleton instance = HungrySingleton.getInstance();//會輸出falseSystem.out.print(hungrySingleton == instance );

    為了避免私有構(gòu)造被反射暴力使用的問題,可以在私有構(gòu)造方法中加多一個判斷instance是否為null,如果不為null,則拋出RuntimeException異常。

    內(nèi)部類單例模式:

    class InnerClassSingleton{private static InnerClassSingleton instance = new InnerClassSingleton(); //構(gòu)造方法私有化private InnerClassSingleton(){}//獲取實例的方法public static InnerClassSingleton getInstance(){ //返回的還是InnerClassSingleton的實例 return SingletonHolder.instance; }//靜態(tài)內(nèi)部類private static class SingletonHolder{private static InnerClassSingleton instance = new InnerClassSingleton();}}

    相對于餓漢式,該方式只在調(diào)用getInstance()方法時才會初始化。是餓漢模式和懶漢模式的結(jié)合。

    枚舉單例模式:

    enmu EnmuSingleton{INSTANCE; public void print(){ System.out.println(“xxx”); }}

    在JVM中生成的INSTANCE實例類型是static final類型的??梢酝ㄟ^calss文件反編譯看到。

    另外,還可以通過反序列化的方式來獲取另一個單例,這兩個“單例”是不相同的,這就相當于對單例模式進行了破壞。

    解決辦法:實現(xiàn)序列化接口,指定serialVersionUID的值。這樣就保證了實例被序列化,反序列化是的版本都是一樣的。否則會報異常。

    還有些類使用到了雙重檢測單例:

    比如:Spring中的ReactiveAdapterRegistry、tomcat中的TomcatURLStreanHandlerFactory.

    OK,單例模式差不多就先學到這里了。

    鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場,版權(quán)歸原作者所有,如有侵權(quán)請聯(lián)系管理員(admin#wlmqw.com)刪除。
    用戶投稿
    上一篇 2022年6月27日 09:17
    下一篇 2022年6月27日 09:17

    相關(guān)推薦

    • 分享4條發(fā)微商朋友圈的方法(微商朋友圈應(yīng)該怎么發(fā))

      對于微商朋友來說,朋友圈的重要性不言而喻了。 那么微商的朋友圈到底該怎么發(fā)呢? 為什么同樣是經(jīng)營一個朋友圈,有的微商看起來逼格滿滿,實際效果也不錯;而有的卻動都不動就被屏蔽甚至拉黑…

      2022年11月27日
    • 英雄聯(lián)盟手游好玩嗎(英雄聯(lián)盟手游好玩還是端游好玩)

      簡要回答 非常好玩,英雄聯(lián)盟手游這款游戲已經(jīng)正式的進行公測,這款游戲是以5v5為模式進行對戰(zhàn)的,它是以英雄聯(lián)盟端游為原型進行開發(fā),里面的每一種玩法基本都沿襲了端游的特點。 01 這…

      2022年11月25日
    • 《寶可夢朱紫》夢特性怎么獲得?隱藏特性獲取方法推薦

      寶可夢朱紫里有很多寶可夢都是擁有夢特性會變強的寶可夢,很多玩家不知道夢特性怎么獲得,下面就給大家?guī)韺毧蓧糁熳想[藏特性獲取方法推薦,感興趣的小伙伴一起來看看吧,希望能幫助到大家。 …

      2022年11月25日
    • 《寶可夢朱紫》奇魯莉安怎么進化?奇魯莉安進化方法分享

      寶可夢朱紫中的奇魯莉安要怎么進化呢?很多玩家都不知道,下面就給大家?guī)韺毧蓧糁熳掀骠斃虬策M化方法分享,感興趣的小伙伴一起來看看吧,希望能幫助到大家。 奇魯莉安進化方法分享 奇魯莉安…

      2022年11月25日
    • cpu性能天梯圖2022 AMD CPU天梯圖最新排行榜出爐

      用戶在DIY自己的主機時選擇CPU是非常關(guān)鍵的,CPU可以說是電腦的大腦,大家也都想追求好一點的CPU來使用,但型號太多了,大部分的用戶都不知道目前哪一款CPU比較好用,快來看看詳…

      2022年11月24日
    • 《寶可夢朱紫》暴飛龍怎么抓?暴飛龍獲得方法

      寶可夢朱紫暴飛龍位置在哪?在游戲中,很多玩家還不清楚暴飛龍具體要怎么樣獲得,其實獲得方法很簡單,暴飛龍直接是沒得抓的,需要玩家從寶貝龍進化得到,下面一起來看一下寶可夢朱紫暴飛龍獲得…

      2022年11月23日
    • 《寶可夢朱紫》布土撥怎么進化?布土撥進化方法介紹

      寶可夢朱紫中,不同的寶可夢有不同的進化方法,其中布土撥的進化方法是比較特殊的。很多玩家不知道寶可夢朱紫布土撥怎么進化,下面就帶來寶可夢朱紫布土撥進化方法介紹,一起來看看吧,希望能幫…

      2022年11月23日
    • 《寶可夢朱紫》薄荷怎么獲得?薄荷獲得方法

      寶可夢朱紫中薄荷有改變寶可夢的屬性或性格等效果,很多玩家想知道寶可夢朱紫薄荷怎么獲得,下面就帶來寶可夢朱紫薄荷獲得方法,感興趣的小伙伴一起來看看吧,希望能幫助到大家。 薄荷獲得方法…

      2022年11月23日
    • 《寶可夢朱紫》怎么交換精靈?交換精靈方法一覽

      寶可夢朱紫中玩家可以和好友或者npc進行交換寶可夢獲得自己沒有的寶可夢,很多玩家想知道寶可夢朱紫怎么交換精靈,下面就帶來寶可夢朱紫交換精靈方法一覽,感興趣的小伙伴不要錯過,希望能幫…

      2022年11月23日
    • 《寶可夢朱紫》龍爪技能怎么獲得?龍爪技能獲取方法

      寶可夢朱紫龍爪技能怎么獲得?在游戲中,很多玩家還不清楚龍爪技能應(yīng)該怎么獲取,其實獲取方法有很多,下面一起來看一下寶可夢朱紫龍爪技能獲取方法,希望可以幫助各位玩家順利的進行游戲內(nèi)容?!?/p>

      2022年11月23日

    聯(lián)系我們

    聯(lián)系郵箱:admin#wlmqw.com
    工作時間:周一至周五,10:30-18:30,節(jié)假日休息