Java多線程編程中易混淆的3個關(guān)鍵字總結(jié)
概述
最近在看《ThinKing In Java》,看到多線程章節(jié)時覺得有一些概念比較容易混淆有必要總結(jié)一下,雖然都不是新的東西,不過還是蠻重要,很基本的,在開發(fā)或閱讀源碼中經(jīng)常會遇到,在這里就簡單的做個總結(jié)。
1.volatile
volatile主要是用來在多線程中同步變量。
在一般情況下,為了提升性能,每個線程在運行時都會將主內(nèi)存中的變量保存一份在自己的內(nèi)存中作為變量副本,但是這樣就很容易出現(xiàn)多個線程中保存的副本變量不一致,或與主內(nèi)存的中的變量值不一致的情況。
而當一個變量被volatile修飾后,該變量就不能被緩存到線程的內(nèi)存中,它會告訴編譯器不要進行任何移出讀取和寫入操作的優(yōu)化,換句話說就是不允許有不同于“主”內(nèi)存區(qū)域的變量拷貝,所以當該變量有變化時,所有調(diào)用該變量的線程都會獲得相同的值,這就確保了該變量在應(yīng)用中的可視性(當一個任務(wù)做出了修改在應(yīng)用中必須是可視的),同時性能也相應(yīng)的降低了(還是比synchronized高)。
但需要注意volatile只能確保操作的是同一塊內(nèi)存,并不能保證操作的原子性。所以volatile一般用于聲明簡單類型變量,使得這些變量具有原子性,即一些簡單的賦值與返回操作將被確保不中斷。但是當該變量的值由自身的上一個決定時,volatile的作用就將失效,這是由volatile關(guān)鍵字的性質(zhì)所決定的。
所以在volatile時一定要謹慎,千萬不要以為用volatile修飾后該變量的所有操作都是原子操作,不再需要synchronized關(guān)鍵字了。
2.ThreadLocal
首先ThreadLocal和本地線程沒有一毛錢關(guān)系,更不是一個特殊的Thread,它只是一個線程的局部變量(其實就是一個Map),ThreadLocal會為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應(yīng)的副本。這樣做其實就是以空間換時間的方式(與synchronized相反),以耗費內(nèi)存為代價,單大大減少了線程同步(如synchronized)所帶來性能消耗以及減少了線程并發(fā)控制的復(fù)雜度。
個人覺得比較典型的例子就是在Android關(guān)于Looper的源碼中對ThreadLocal的使用,同時也包含了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ā)訪問,ThreadLocal與synchronized還是有本質(zhì)的區(qū)別。synchronized是利用鎖的機制,使變量或代碼塊在某一時該只能被一個線程訪問。而ThreadLocal為每一個線程都提供了變量的副本,使得每個線程在某一時間訪問到的并不是同一個對象,這樣就隔離了多個線程對數(shù)據(jù)的數(shù)據(jù)共享。而Synchronized卻正好相反,它用于在多個線程間通信時能夠獲得數(shù)據(jù)共享。即Synchronized用于線程間的數(shù)據(jù)共享,而ThreadLocal則用于線程間的數(shù)據(jù)隔離。所以ThreadLocal并不能代替synchronized,Synchronized的功能范圍更廣(同步機制)。
3.synchronized
synchronized關(guān)鍵字是Java利用鎖的機制自動實現(xiàn)的,一般有同步方法和同步代碼塊兩種使用方式。Java中所有的對象都自動含有單一的鎖(也稱為監(jiān)視器),當在對象上調(diào)用其任意的synchronized方法時,此對象被加鎖(一個任務(wù)可以多次獲得對象的鎖,計數(shù)會遞增),同時在線程從該方法返回之前,該對象內(nèi)其他所有要調(diào)用類中被標記為synchronized的方法的線程都會被阻塞。當然針對每個類也有一個鎖(作為類的Class對象的一部分),所以你懂的^.^。
最后需要注意的是synchronized是同步機制中最安全的一種方式,其他的任何方式都是有風(fēng)險的,當然付出的代價也是最大的。
相關(guān)文章
Spring Boot集成springfox-swagger2構(gòu)建restful API的方法教程
這篇文章主要給大家介紹了關(guān)于Spring Boot集成springfox-swagger2構(gòu)建restful API的相關(guān)資料,文中介紹的非常詳細,對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。2017-06-06SpringCloud+Redis實現(xiàn)Api接口限流防止惡意刷接口
接口限流是為了保護系統(tǒng)和服務(wù),防止因為過多的請求而崩潰,本文主要介紹了SpringCloud+Redis實現(xiàn)Api接口限流防止惡意刷接口,具有一定的參考價值,感興趣的可以了解一下2024-03-03SpringBoot集成Mybatis-plus并實現(xiàn)自動生成相關(guān)文件的示例代碼
Mybatis-Plus是一個優(yōu)秀的Mybatis增強工具,目前更新到3.1.1,本文通過示例代碼給大家介紹SpringBoot集成Mybatis-plus并實現(xiàn)自動生成相關(guān)文件的問題,感興趣的朋友跟隨小編一起看看吧2021-12-12