对synchronized的一点理解
一、synchronized的使用
(一)、synchronized同步方法
1. “非線程安全”問題存在于“實(shí)例變量”中,如果是方法內(nèi)部的私有變量,則不存在“非線程安全”問題。
2. 如果多個線程共同訪問1個對象中的實(shí)例變量,則有可能出現(xiàn)“非線程安全”問題。
3. synchronized取得的鎖都是對象鎖,而不是把一段代碼或方法當(dāng)做鎖。因此在多線程訪問同一個對象時,哪個線程先執(zhí)行synchronized關(guān)鍵字的方法,哪個線程就持有該方法所屬對象的鎖lock,其他線程只能呈等待狀態(tài)。
但如果多個線程訪問多個對象,則JVM會創(chuàng)建多個鎖。
4. 調(diào)用關(guān)鍵字synchronized聲明的方法一定是排隊(duì)運(yùn)行的。另外只有共享資源的讀寫訪問才需要同步化,如果不是共享資源,就沒有同步化的必要。
5. 臟讀。當(dāng)A線程調(diào)用anyObject對象加入synchronized關(guān)鍵字的X方法時,A線程就獲得了X方法鎖,更準(zhǔn)確地講,是獲得了對象的鎖,所以其他線程必須等A線程執(zhí)行完畢才可以調(diào)用X方法,但B線程可以隨意調(diào)用其他的非synchronized關(guān)鍵字的同步方法。
6. synchronized鎖重入。使用synchronized時,當(dāng)一個線程得到一個對象鎖后,再次請求此對象鎖時,是可以再次得到該對象的鎖的。
7. 當(dāng)一個線程執(zhí)行的代碼出現(xiàn)異常時,其所持有的鎖會自動釋放。
8. 同步不可以繼承。如果父類方法使用了關(guān)鍵字synchronized,那么子類方法不會繼承synchronized,如果子類需要同步,還需要在子類方法中添加synchronized。
(二)、synchronized同步代碼塊
1. 同一個方法中,不在synchronized塊中的就是異步執(zhí)行,在synchronized塊中的就是同步執(zhí)行。
2. 當(dāng)一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對同一個object中所有其他synchronized(this)同步代碼塊的訪問將被阻塞,這是因?yàn)閟ynchronized使用的是“對象監(jiān)視器”。
3. 和synchronized方法一樣,synchronized(this)代碼塊也是鎖定當(dāng)前對象。
4. java還支持對“任意對象”作為“對象監(jiān)視器”來實(shí)現(xiàn)同步的功能。這個任意對象大多數(shù)是實(shí)例變量及方法的參數(shù),使用格式為:synchronized(非this對象x),此時將x對象本身作為“對象監(jiān)視器”
鎖非this對象的優(yōu)點(diǎn):如果在一個類中有很多個synchronized方法,這時雖然能實(shí)現(xiàn)同步,但會受到阻塞,所以影響運(yùn)行效率;但如果使用同步代碼塊鎖非this對象,則synchronized(非this對象x)代碼塊中的程序與同步方法是異步的,不與其他鎖this同步方法爭搶this鎖,可以大大提高運(yùn)行效率。
5. synchronized還可以應(yīng)用在static靜態(tài)方法上,如果這樣寫,就是對當(dāng)前的*.java文件對應(yīng)的class類進(jìn)行加鎖。
跟synchronized加到非static方法上的區(qū)別:synchronized加到static靜態(tài)方法上,是給Class類上鎖;而synchronized加到非static靜態(tài)方法上,是給對象上鎖。
6. 由于JVM具有String常量池緩存的功能,因此在大多數(shù)情況下,同步synchronized代碼塊都不使用String作為鎖對象:synchronized(string),而改用其他的,比如new Object實(shí)例化一個Object對象,但它并不放入緩存中。
以上方法的具體代碼例子可以參考《java多線程編程核心技術(shù)》
二、synchronized的實(shí)現(xiàn)原理
目前在Java中存在兩種鎖機(jī)制:synchronized和Lock
synchronized實(shí)現(xiàn)同步的基礎(chǔ):Java中每一個對象都可以作為鎖,具體表現(xiàn)為以下3種方式:(Synchronized鎖, 鎖住的是對象。)
1. 對于普通同步方法,鎖是當(dāng)前實(shí)例對象
2. 對于靜態(tài)同步方法,鎖是當(dāng)前類的class對象
3. 對于同步方法塊,鎖是synchronized括號里配置的對象
synchronized用的鎖是存在java對象頭里的。重量級鎖、輕量級鎖和偏向鎖之間的轉(zhuǎn)換:
Synchronized同步塊和同步方法在實(shí)現(xiàn)上的區(qū)別:
在下面的例子中,使用了同步塊和同步方法,通過使用javap工具查看生成的class文件信息,來分析synchronized關(guān)鍵字的實(shí)現(xiàn)細(xì)節(jié)。
public class Synchronized {public static void main(String[] args) {// 對Synchronized Class對象進(jìn)行加鎖synchronized (Synchronized.class) {}// 靜態(tài)同步方法,對Synchronized Class對象進(jìn)行加鎖 m();}public static synchronized void m() {} }在synchronized.class的同級目錄下執(zhí)行:javap -v synchronized.class,部分輸出如下:
說明:
public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATIC //方法修飾符,表示public staticCode:stack=2, locals=1, args_size=10: ldc #1 // class testnew/Synchronized2: dup 3: monitorenter // monitorenter:監(jiān)視器進(jìn)入,獲取鎖4: monitorexit // monitorexit: 監(jiān)視器退出,釋放鎖5: invokestatic #16 // Method m:()V8: returnpublic static synchronized void m();descriptor: ()Vflags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED //方法修飾符,表示public static synchronizedCode: stack=0, locals=0, args_size=0 0: return對于同步塊的實(shí)現(xiàn)使用了monitorenter和monitorexit指令,而同步方法則是依靠方法修飾符上的ACC_SYNCHRONIZED來完成的。無論采用哪種方法,其本質(zhì)是對一個對象的監(jiān)視器(monitor)進(jìn)行獲取,而這個獲取過程是排他的,也就是同一個時刻只能有一個線程獲取到由synchronized所保護(hù)對象的監(jiān)視器。
任意一個對象都擁有自己的監(jiān)視器,當(dāng)這個對象由同步塊或者這個對象的同步方法調(diào)用時,執(zhí)行方法的線程必須先獲取到該對象的監(jiān)視器才能進(jìn)入同步塊或者同步方法,而沒有獲取到監(jiān)視器(執(zhí)行該方法)的線程將會被阻塞在同步塊和同步方法的入口處,進(jìn)入BLOCKED狀態(tài)。
下圖描述了對象、對象的監(jiān)視器、同步隊(duì)列和執(zhí)行線程之間的關(guān)系。
可以看出,任意線程對Object(Object由synchronized保護(hù))的方法,首先要獲得Object的監(jiān)視器。如果獲取失敗,線程進(jìn)入同步隊(duì)列,線程狀態(tài)變?yōu)锽LOCKED。當(dāng)訪問Object的前驅(qū)(獲得了鎖的線程)釋放了鎖,則該釋放操作喚醒阻塞在同步隊(duì)列總的線程,使其重新嘗試對監(jiān)視器的獲取。
相關(guān)閱讀:java中的鎖分類?
參考:
《Java并發(fā)編程的藝術(shù)》
Java中的鎖機(jī)制 synchronized & 偏向鎖 & 輕量級鎖 & 重量級鎖 & 各自優(yōu)缺點(diǎn)及場景 & AtomicReference?
轉(zhuǎn)載于:https://www.cnblogs.com/zeroingToOne/p/8967445.html
總結(jié)
以上是生活随笔為你收集整理的对synchronized的一点理解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 结合源码分析 bubble 使用注意事项
- 下一篇: HDU 3549 Flow Proble