Java多線程編程中易混淆的3個(gè)關(guān)鍵字總結(jié)
概述
最近在看《ThinKing In Java》,看到多線程章節(jié)時(shí)覺(jué)得有一些概念比較容易混淆有必要總結(jié)一下,雖然都不是新的東西,不過(guò)還是蠻重要,很基本的,在開發(fā)或閱讀源碼中經(jīng)常會(huì)遇到,在這里就簡(jiǎn)單的做個(gè)總結(jié)。
1.volatile
volatile主要是用來(lái)在多線程中同步變量。
在一般情況下,為了提升性能,每個(gè)線程在運(yùn)行時(shí)都會(huì)將主內(nèi)存中的變量保存一份在自己的內(nèi)存中作為變量副本,但是這樣就很容易出現(xiàn)多個(gè)線程中保存的副本變量不一致,或與主內(nèi)存的中的變量值不一致的情況。
而當(dāng)一個(gè)變量被volatile修飾后,該變量就不能被緩存到線程的內(nèi)存中,它會(huì)告訴編譯器不要進(jìn)行任何移出讀取和寫入操作的優(yōu)化,換句話說(shuō)就是不允許有不同于“主”內(nèi)存區(qū)域的變量拷貝,所以當(dāng)該變量有變化時(shí),所有調(diào)用該變量的線程都會(huì)獲得相同的值,這就確保了該變量在應(yīng)用中的可視性(當(dāng)一個(gè)任務(wù)做出了修改在應(yīng)用中必須是可視的),同時(shí)性能也相應(yīng)的降低了(還是比synchronized高)。
但需要注意volatile只能確保操作的是同一塊內(nèi)存,并不能保證操作的原子性。所以volatile一般用于聲明簡(jiǎn)單類型變量,使得這些變量具有原子性,即一些簡(jiǎn)單的賦值與返回操作將被確保不中斷。但是當(dāng)該變量的值由自身的上一個(gè)決定時(shí),volatile的作用就將失效,這是由volatile關(guān)鍵字的性質(zhì)所決定的。
所以在volatile時(shí)一定要謹(jǐn)慎,千萬(wàn)不要以為用volatile修飾后該變量的所有操作都是原子操作,不再需要synchronized關(guān)鍵字了。
2.ThreadLocal
首先ThreadLocal和本地線程沒(méi)有一毛錢關(guān)系,更不是一個(gè)特殊的Thread,它只是一個(gè)線程的局部變量(其實(shí)就是一個(gè)Map),ThreadLocal會(huì)為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,所以每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對(duì)應(yīng)的副本。這樣做其實(shí)就是以空間換時(shí)間的方式(與synchronized相反),以耗費(fèi)內(nèi)存為代價(jià),單大大減少了線程同步(如synchronized)所帶來(lái)性能消耗以及減少了線程并發(fā)控制的復(fù)雜度。
個(gè)人覺(jué)得比較典型的例子就是在Android關(guān)于Looper的源碼中對(duì)ThreadLocal的使用,同時(shí)也包含了ThreadLocal的基本用法,具體代碼如下:
public class Looper {
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
private static final ThreadLocal sThreadLocal = new ThreadLocal();
......
private static Looper mMainLooper = null;
......
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
......
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
......
}
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
public synchronized static final Looper getMainLooper() {
return mMainLooper;
}
......
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
......
}
但需要注意的是,雖然ThreadLocal和Synchonized都用于解決多線程并發(fā)訪問(wèn),ThreadLocal與synchronized還是有本質(zhì)的區(qū)別。synchronized是利用鎖的機(jī)制,使變量或代碼塊在某一時(shí)該只能被一個(gè)線程訪問(wèn)。而ThreadLocal為每一個(gè)線程都提供了變量的副本,使得每個(gè)線程在某一時(shí)間訪問(wèn)到的并不是同一個(gè)對(duì)象,這樣就隔離了多個(gè)線程對(duì)數(shù)據(jù)的數(shù)據(jù)共享。而Synchronized卻正好相反,它用于在多個(gè)線程間通信時(shí)能夠獲得數(shù)據(jù)共享。即Synchronized用于線程間的數(shù)據(jù)共享,而ThreadLocal則用于線程間的數(shù)據(jù)隔離。所以ThreadLocal并不能代替synchronized,Synchronized的功能范圍更廣(同步機(jī)制)。
3.synchronized
synchronized關(guān)鍵字是Java利用鎖的機(jī)制自動(dòng)實(shí)現(xiàn)的,一般有同步方法和同步代碼塊兩種使用方式。Java中所有的對(duì)象都自動(dòng)含有單一的鎖(也稱為監(jiān)視器),當(dāng)在對(duì)象上調(diào)用其任意的synchronized方法時(shí),此對(duì)象被加鎖(一個(gè)任務(wù)可以多次獲得對(duì)象的鎖,計(jì)數(shù)會(huì)遞增),同時(shí)在線程從該方法返回之前,該對(duì)象內(nèi)其他所有要調(diào)用類中被標(biāo)記為synchronized的方法的線程都會(huì)被阻塞。當(dāng)然針對(duì)每個(gè)類也有一個(gè)鎖(作為類的Class對(duì)象的一部分),所以你懂的^.^。
最后需要注意的是synchronized是同步機(jī)制中最安全的一種方式,其他的任何方式都是有風(fēng)險(xiǎn)的,當(dāng)然付出的代價(jià)也是最大的。
相關(guān)文章
解決springboot bean中大寫的字段返回變成小寫的問(wèn)題
這篇文章主要介紹了解決springboot bean中大寫的字段返回變成小寫的問(wèn)題,具有很好的參考價(jià)值希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01
Java中g(shù)etSuperclass()方法的使用與原理解讀
文章介紹了Java中的getSuperclass()方法,該方法用于獲取一個(gè)類的直接父類,通過(guò)理解其使用方式、工作原理以及實(shí)際應(yīng)用場(chǎng)景,可以更好地利用反射機(jī)制處理類的繼承關(guān)系,實(shí)現(xiàn)動(dòng)態(tài)類型檢查、類加載以及序列化等功能2025-01-01
java使用定時(shí)器實(shí)現(xiàn)監(jiān)聽數(shù)據(jù)變化
這篇文章主要為大家詳細(xì)介紹了Java如何使用定時(shí)器監(jiān)聽數(shù)據(jù)變化,當(dāng)滿足某個(gè)條件時(shí)(例如沒(méi)有數(shù)據(jù)更新)自動(dòng)執(zhí)行某項(xiàng)任務(wù),有興趣的可以了解下2023-11-11
Java橋梁設(shè)計(jì)模式優(yōu)雅地將抽象與實(shí)現(xiàn)分離
Java橋接設(shè)計(jì)模式通過(guò)將抽象和實(shí)現(xiàn)分離,使得它們可以獨(dú)立地變化,從而實(shí)現(xiàn)更靈活的代碼結(jié)構(gòu)。它是一種優(yōu)雅的設(shè)計(jì)模式,適用于需要處理多個(gè)變化因素的復(fù)雜應(yīng)用程序2023-04-04
解決Java處理HTTP請(qǐng)求超時(shí)的問(wèn)題
這篇文章主要介紹了解決Java處理HTTP請(qǐng)求超時(shí)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03
通過(guò)FeignClient如何獲取文件流steam?is?close問(wèn)題
這篇文章主要介紹了通過(guò)FeignClient如何獲取文件流steam?is?close問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06

