摘要:在構(gòu)建一個對象的過程中,更要考慮到多線程間共享數(shù)據(jù)的一致性問題,否則很可能會發(fā)生一個在線程中構(gòu)建完整的對象,在線程中看到的卻只被構(gòu)建了一部分。例如下面的代碼上面的代碼本意是想實現(xiàn)一個單例模式,但在多線程環(huán)境下,這個單例模式將很容易被打破。
在上一篇文章《從Java多線程可見性談Happens-Before原則》中,我們詳細討論了在并發(fā)編程中Happens-Before原則對多線程共享變量的重要性。要想確保讓一個線程對共享變量的修改能被其它線程感知到,就必須讓兩個線程中的操作滿足Happens-Before原則。
在構(gòu)建一個對象的過程中,更要考慮到多線程間共享數(shù)據(jù)的一致性問題,否則很可能會發(fā)生一個在A線程中構(gòu)建完整的對象,在B線程中看到的卻只被構(gòu)建了一部分。例如下面的代碼:
public class UnsafeLazyInitialization { private static Resource resource; public static Resource getInstance() { if(resource == null) { resource = new Resource(); } return resource; } }
上面的代碼本意是想實現(xiàn)一個單例模式,但在多線程環(huán)境下,這個單例模式將很容易被打破。
首先,resource是一個普通變量,當(dāng)一個線程更新resource變量的值時,其它線程可能無法感知到resource引用已經(jīng)指向了一個創(chuàng)建好的對象,所以可能會導(dǎo)致程序創(chuàng)建多個Resource對象。
更糟糕的是重排序,在線程A中是先初始化Resource對象的各個field之后再將resource引用設(shè)置為指向它,線程B看到的可能是對引用變量resource的寫入操作在對Resource對象各個field的寫入操作之前發(fā)生。這會導(dǎo)致線程B看到的是一個被部分構(gòu)造的Resource對象實例,該對象可能處于無效狀態(tài)。
為了解決上面的問題,我們可以套用Happens-Before規(guī)則。例如在getInstance方法上聲明synchronized。
然而對于構(gòu)建一個對象實例的操作,除了可以使用Happens-Before原則外,我們利用下面兩種方式也可以保證對象的構(gòu)建狀態(tài)被正確發(fā)布到其它線程。
靜態(tài)初始化器是由JVM在類的初始化階段執(zhí)行,即在類被加載后并且被線程使用前。在靜態(tài)初始化期間,內(nèi)存寫入操作將自動對所有線程可見。所以上面的程序改下成如下代碼將會確保Resource對象被正確發(fā)布到其它線程:
public class EagerInitialization { private static Resource resource = new Resource(); public static Resource getInstance() { return resource; } }含有final域的對象
對于含有final域的對象,初始化安全性可以防止對對象引用的寫入操作被重排序到對象構(gòu)造過程之前。在構(gòu)造函數(shù)完成時,構(gòu)造函數(shù)對final域的所有寫入操作,以及通過final域可以到達的任何變量的寫入操作,都能夠被獲取到該對象引用的線程看到。例如下面的代碼:
public class FinalInitialization { private static final Resource resource; public static Resource getInstance() { if(resource == null) { resource = new Resource(); } return resource; } }
在上面的代碼中,將resource變量聲明為final類型的。這樣可以保證,無論哪個線程,只要獲取到對象的引用的值,就一定可以看到一個被完整構(gòu)建的Resource對象。
但是此處的resource引用變量本身仍然需要其他機制保證可以對其它線程可見。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/67704.html
摘要:多線程環(huán)境下的一些問題安全性問題在沒有正確同步的情況下,多線程環(huán)境下程序可能得出錯誤的結(jié)果。一些相關(guān)概念競爭條件多線程的環(huán)境下,程序執(zhí)行的結(jié)果取決于線程交替執(zhí)行的方式。而線程的交替操作順序是不可預(yù)測的,如此程序執(zhí)行的結(jié)果也是不可預(yù)測的。 入口 Java多線程的應(yīng)用復(fù)雜性之如jvm有限的幾個內(nèi)存方面的操作和規(guī)范,就像無數(shù)紛繁復(fù)雜的應(yīng)用邏輯建立在有限的指令集上。 如何寫出線程安全的程序,有...
摘要:概述集合類主要有大分支,及。不能保證元素的排列順序,順序有可能發(fā)生變化不是同步的集合元素可以是但只能放入一個是接口的唯一實現(xiàn)類,可以確保集合元素處于排序狀態(tài)。如果這兩個的通過比較返回,新添加的將覆蓋集合中原有的,但不會覆蓋。 概述 Java集合類主要有2大分支,Collection及Map。Collection體系如下: https://upload-images.jianshu......
摘要:下面是線程相關(guān)的熱門面試題,你可以用它來好好準備面試。線程安全問題都是由全局變量及靜態(tài)變量引起的。持有自旋鎖的線程在之前應(yīng)該釋放自旋鎖以便其它線程可以獲得自旋鎖。 最近看到網(wǎng)上流傳著,各種面試經(jīng)驗及面試題,往往都是一大堆技術(shù)題目貼上去,而沒有答案。 不管你是新程序員還是老手,你一定在面試中遇到過有關(guān)線程的問題。Java語言一個重要的特點就是內(nèi)置了對并發(fā)的支持,讓Java大受企業(yè)和程序員...
摘要:包含了支持服務(wù)開發(fā)的類,并為提供基礎(chǔ),如語言基礎(chǔ)操作操作網(wǎng)絡(luò)通信以及多線程等技術(shù)。在運行文件時,的解釋器對這些字節(jié)碼進行解釋執(zhí)行,執(zhí)行過程中需要加入的類在連接階段被載入到運行環(huán)境中。支持多個線程同時執(zhí)行,并提供多線程之間的同步機制。 1.什么是Java語言 簡單地說,Java 是由 Sun Microsystems 公司于 1995 年推出的一門面向?qū)ο蟪绦蛟O(shè)計語言。2009 年 Or...
閱讀 1113·2021-11-22 14:56
閱讀 1575·2019-08-30 15:55
閱讀 3410·2019-08-30 15:45
閱讀 1681·2019-08-30 13:03
閱讀 2896·2019-08-29 18:47
閱讀 3366·2019-08-29 11:09
閱讀 2671·2019-08-26 18:36
閱讀 2640·2019-08-26 13:55