亚洲免费在线视频-亚洲啊v-久久免费精品视频-国产精品va-看片地址-成人在线视频网

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

Java Volatile應(yīng)用單例模式實(shí)現(xiàn)過(guò)程解析

瀏覽:46日期:2022-08-21 15:29:08

單例模式

回顧一下,單線程下的單例模式代碼

餓漢式

構(gòu)造器私有化 自行創(chuàng)建,并且用靜態(tài)變量保存static 向外提供這個(gè)實(shí)例 public 強(qiáng)調(diào)這是一個(gè)單例,用final

public class sington(){ public final static INSTANCE = new singleton(); private singleton(){}}

第二種:jdk1.5之后用枚舉類型

枚舉類型:表示該類型的對(duì)象是有限的幾個(gè)

我們可以限定為1個(gè),就稱了單例

public enum Singleto{ INSTANCE}

第三種靜態(tài)代碼塊

public class Singleton{public final static INSTANCE;static{ INSTANCE = new Singleton();}private Singleton(){}}

懶漢式構(gòu)造器私有化

用一個(gè)靜態(tài)變量保存這個(gè)唯一實(shí)例

提供一個(gè)靜態(tài)方法,獲取這個(gè)實(shí)例

public class Singleton{ private static Singleton INSTANCE; private Singleton(){} public static Singleton getInstance(){ if(instance==null){ INSTANCE = new Singleton(); } return INSTANCE; }}

public class SingletonDemo { private static SingletonDemo instance = null; private SingletonDemo () { System.out.println(Thread.currentThread().getName() + 't 我是構(gòu)造方法SingletonDemo'); } public static SingletonDemo getInstance() { if(instance == null) { instance = new SingletonDemo(); } return instance; } public static void main(String[] args) { // 這里的 == 是比較內(nèi)存地址 System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance()); System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance()); System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance()); System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance()); }}

最后輸出結(jié)果:

Java Volatile應(yīng)用單例模式實(shí)現(xiàn)過(guò)程解析

但是在多線程的環(huán)境下,我們的單例模式是否還是同一個(gè)對(duì)象了

public class SingletonDemo { private static SingletonDemo instance = null; private SingletonDemo () { System.out.println(Thread.currentThread().getName() + 't 我是構(gòu)造方法SingletonDemo'); } public static SingletonDemo getInstance() { if(instance == null) { instance = new SingletonDemo(); } return instance; } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(() -> {SingletonDemo.getInstance(); }, String.valueOf(i)).start(); } }}

從下面的結(jié)果我們可以看出,我們通過(guò)SingletonDemo.getInstance() 獲取到的對(duì)象,并不是同一個(gè),而是被下面幾個(gè)線程都進(jìn)行了創(chuàng)建,那么在多線程環(huán)境下,單例模式如何保證呢?

Java Volatile應(yīng)用單例模式實(shí)現(xiàn)過(guò)程解析

解決方法一

引入synchronized關(guān)鍵字

public synchronized static SingletonDemo getInstance() { if(instance == null) { instance = new SingletonDemo(); } return instance;}

輸出結(jié)果:

Java Volatile應(yīng)用單例模式實(shí)現(xiàn)過(guò)程解析

我們能夠發(fā)現(xiàn),通過(guò)引入Synchronized關(guān)鍵字,能夠解決高并發(fā)環(huán)境下的單例模式問(wèn)題

但是synchronized屬于重量級(jí)的同步機(jī)制,它只允許一個(gè)線程同時(shí)訪問(wèn)獲取實(shí)例的方法,但是為了保證數(shù)據(jù)一致性,而減低了并發(fā)性,因此采用的比較少

解決方法二

通過(guò)引入DCL Double Check Lock雙端檢鎖機(jī)制

public static SingletonDemo getInstance() { if(instance == null) { // 同步代碼段的時(shí)候,進(jìn)行檢測(cè) synchronized (SingletonDemo.class) {if(instance == null) { instance = new SingletonDemo();} } } return instance; }

最后輸出的結(jié)果為:

Java Volatile應(yīng)用單例模式實(shí)現(xiàn)過(guò)程解析

從輸出結(jié)果來(lái)看,確實(shí)能夠保證單例模式的正確性,但是上面的方法還是存在問(wèn)題的

DCL(雙端檢鎖)機(jī)制不一定是線程安全的,原因是有指令重排的存在,加入volatile可以禁止指令重排

原因是在某一個(gè)線程執(zhí)行到第一次檢測(cè)的時(shí)候,讀取到 instance 不為null,instance的引用對(duì)象可能沒(méi)有完成實(shí)例化。因?yàn)?instance = new SingletonDemo();可以分為以下三步進(jìn)行完成:

memory = allocate(); // 1、分配對(duì)象內(nèi)存空間 instance(memory); // 2、初始化對(duì)象 instance = memory; // 3、設(shè)置instance指向剛剛分配的內(nèi)存地址,此時(shí)instance != null

但是我們通過(guò)上面的三個(gè)步驟,能夠發(fā)現(xiàn),步驟2 和 步驟3之間不存在 數(shù)據(jù)依賴關(guān)系,而且無(wú)論重排前 還是重排后,程序的執(zhí)行結(jié)果在單線程中并沒(méi)有改變,因此這種重排優(yōu)化是允許的。

memory = allocate(); // 1、分配對(duì)象內(nèi)存空間 instance = memory; // 3、設(shè)置instance指向剛剛分配的內(nèi)存地址,此時(shí)instance != null,但是對(duì)象還沒(méi)有初始化完成 instance(memory); // 2、初始化對(duì)象

這樣就會(huì)造成什么問(wèn)題呢?

也就是當(dāng)我們執(zhí)行到重排后的步驟2,試圖獲取instance的時(shí)候,會(huì)得到null,因?yàn)閷?duì)象的初始化還沒(méi)有完成,而是在重排后的步驟3才完成,因此執(zhí)行單例模式的代碼時(shí)候,就會(huì)重新在創(chuàng)建一個(gè)instance實(shí)例

指令重排只會(huì)保證串行語(yǔ)義的執(zhí)行一致性(單線程),但并不會(huì)關(guān)系多線程間的語(yǔ)義一致性

所以當(dāng)一條線程訪問(wèn)instance不為null時(shí),由于instance實(shí)例未必已初始化完成,這就造成了線程安全的問(wèn)題

所以需要引入volatile,來(lái)保證出現(xiàn)指令重排的問(wèn)題,從而保證單例模式的線程安全性

private static volatile SingletonDemo instance = null;

最終代碼

public class SingletonDemo { private static volatile SingletonDemo instance = null; private SingletonDemo () { System.out.println(Thread.currentThread().getName() + 't 我是構(gòu)造方法SingletonDemo'); } public static SingletonDemo getInstance() { if(instance == null) { // a 雙重檢查加鎖多線程情況下會(huì)出現(xiàn)某個(gè)線程雖然這里已經(jīng)為空,但是另外一個(gè)線程已經(jīng)執(zhí)行到d處 synchronized (SingletonDemo.class) //b { //c不加volitale關(guān)鍵字的話有可能會(huì)出現(xiàn)尚未完全初始化就獲取到的情況。原因是內(nèi)存模型允許無(wú)序?qū)懭雐f(instance == null) { // d 此時(shí)才開(kāi)始初始化 instance = new SingletonDemo();} } } return instance; } public static void main(String[] args) {// // 這里的 == 是比較內(nèi)存地址// System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());// System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());// System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());// System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance()); for (int i = 0; i < 10; i++) { new Thread(() -> {SingletonDemo.getInstance(); }, String.valueOf(i)).start(); } }}

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持好吧啦網(wǎng)。

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 日韩欧美国产亚洲 | 九九久久久久久久爱 | 99在线在线视频免费视频观看 | 正在播放国产乱子伦视频 | 九月婷婷亚洲综合在线 | 国产精品久久久久国产精品三级 | 成人免费观看国产高清 | 国产成人精品视频 | 亚洲精品成人久久久影院 | 杨幂精品国产专区91在线 | 日本三级网站在线线观看 | 国产精品久久久久久久久久直 | 色偷偷在线刺激免费视频 | 一级特一级特色生活片 | 99国产精品一区二区 | 日本亚洲视频 | 国产毛片一区二区三区精品 | 午夜三级网 | 久久性生大片免费观看性 | 精品国产_亚洲人成在线高清 | 日韩欧免费一区二区三区 | 国产精品天天爽夜夜欢张柏芝 | 国产精品blacked在线 | 91福利精品老师国产自产在线 | 国产在线一区二区 | 欧美日韩国产综合一区二区三区 | 成人毛片高清视频观看 | 特级生活片| 欧美视频一区二区三区 | 碰碰碰精品视频在线观看 | 久久亚洲国产成人亚 | 欧美在线看欧美高清视频免费 | 三级毛片免费看 | 精品久久久久久中文字幕网 | 国产成人亚洲精品一区二区在线看 | 亚洲国产高清人在线 | 国产一区二区三区四区五区 | 日韩毛片 | 亚洲天堂在线观看视频 | 国产成人免费在线视频 | 97香蕉久久夜色精品国产 |