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