免费爱碰视频在线观看,九九精品国产屋,欧美亚洲尤物久久精品,1024在线观看视频亚洲

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

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

      意圖:保證每個(gè)類,只有一個(gè)實(shí)例,并且提供一個(gè)全局的訪問點(diǎn)

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

      雙重檢測加鎖的單例實(shí)現(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,先擴(kuò)展一下筆記synchronized的四種用法

      >>>

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

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

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

      舉個(gè)例子:

      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個(gè)新對象到數(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()+"運(yùn)行中…"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結(jié)束…"); } }}

      運(yùn)行結(jié)果:

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

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

      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ù)組里的對象都是同一個(gè),前面是5個(gè)不一樣的 } 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()+"運(yùn)行中…"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結(jié)束…"); } }}

      運(yùn)行結(jié)果:

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

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

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

      二、synchronized同步鎖為類

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

      上面的例子我改改:

      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()+"運(yùn)行中…"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結(jié)束…"); } }}

      運(yùn)行結(jié)果:

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

      這些線程同樣實(shí)現(xiàn)了同步,因?yàn)樗麄兊耐芥i是同一個(gè)對象SyncTest類對象。

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

      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個(gè)線程 } sync1.start(); //普通對象鎖跑一個(gè)線程 }}class Sync extends Thread{ public void run(){ synchronized (SyncTest.class) { //類對象鎖 System.out.println(Thread.currentThread().getName()+"運(yùn)行中…"); 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()+"運(yùn)行中…"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"結(jié)束…"); } }}

      運(yùn)行結(jié)果:

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

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

      三、synchronized修飾普通方法

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

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

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

      看代碼:

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

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

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

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

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

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

      總結(jié)

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

      >>>

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

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

      step1 開辟空間

      step2 初始化空間

      step3 賦值

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

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

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

      private volatile static LazySingleton instance;

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

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

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

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

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

      類加載過程:

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

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

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

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

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

      答案:不是

      反射獲取:

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

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

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

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

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

      枚舉單例模式:

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

      在JVM中生成的INSTANCE實(shí)例類型是static final類型的。可以通過calss文件反編譯看到。

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

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

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

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

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

      鄭重聲明:本文內(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)營一個(gè)朋友圈,有的微商看起來逼格滿滿,實(shí)際效果也不錯(cuò);而有的卻動(dòng)都不動(dòng)就被屏蔽甚至拉黑…

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

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

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

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

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

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

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

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

        2022年11月24日
      • 《寶可夢朱紫》暴飛龍?jiān)趺醋??暴飛龍獲得方法

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

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

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

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

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

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

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

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

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

        2022年11月23日

      聯(lián)系我們

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