Synchronized關(guān)鍵字詳解(對(duì)object對(duì)象持鎖)

1.Synchronized對(duì)象監(jiān)視器為Object時(shí)的使用
1)Synchronized修飾方法時(shí),持有當(dāng)前對(duì)象的鎖芹彬,當(dāng)有多個(gè)對(duì)象時(shí)县遣,不同的對(duì)象有不同對(duì)象的“監(jiān)視器”。
2)當(dāng)Synchronized修飾類中的一個(gè)方法熊榛,而該類中的另一個(gè)方法沒(méi)被Synchronized修飾時(shí),這種情況下當(dāng)A線程先調(diào)用被Synchronized修飾的方法時(shí)腕巡,A線程就先持有object對(duì)象的Lock鎖玄坦,但是B線程可以異步調(diào)用object對(duì)象中的非Synchronized類型的方法。
如果B線程在A線程調(diào)用Synchronized方法后再調(diào)用Synchronized方法绘沉,就需要等待煎楣,也就是同步。

2.臟讀:發(fā)生臟讀的情況是在讀取實(shí)例變量時(shí)车伞,此值已經(jīng)被其他線程更改過(guò)了择懂。
以下是一個(gè)臟讀情況:PublicVar類中setValue被synchronized修飾,但是getValue沒(méi)被synchronized修飾另玖,在setValue的方法中將username修改后使線程sleep5000ms困曙。這時(shí)username被更改但是userpassword沒(méi)有被更改,此時(shí)主線程中的getValue()讀取了數(shù)據(jù)讀到的是被更改的username “B” 和沒(méi)被更改的userpassword “AA”谦去。

public class PublicVar{
    public String username = "A";
    public String password = "AA";
    synchronized public void setValue(String username,String password){
        try {
            this.username = username;
            Thread.sleep(5000);
            this.password = password;
            System.out.println("setValue method thread name=" + Thread.currentThread().getName() + " username=" + username + " password=" + password);
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void getValue(){
        System.out.println("getValue method thread name=" + Thread.currentThread().getName() + " username=" + username + " password=" + password);
    }
}
public class ThreadA extends Thread{
    private PublicVar publicVar;
    public ThreadA(publicVar publicVar){
        super();
        this.publicVar = publicVar;
    }
    @Override
    public void run(){
        super.run();
        publicVar.setValue("B","BB");
    } 
}
public class Test{
    public static void main(String[] args){
        try{
            PublicVar publicVarRef = new PublicVar()慷丽;
            ThreadA thread = new ThreadA(publicVarRef);
            thread.start();
            thread.sleep(200);
            publicVarRef.getValue();
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

該臟讀的解決方法就是在getValue()方法前也加上synchronized關(guān)鍵字鳄哭,這樣一來(lái)當(dāng)A線程調(diào)用anyObject對(duì)象加入synchronized關(guān)鍵字的X方法時(shí)要糊,A線程就獲得了X方法所在對(duì)象的鎖,所以其他線程必須等A線程執(zhí)行完畢才可以調(diào)用X方法妆丘,而B(niǎo)線程如果調(diào)用聲明了synchronized關(guān)鍵字的非X方法時(shí)锄俄,必須等A線程將X方法執(zhí)行完,也就是釋放對(duì)象鎖后才可以調(diào)用飘痛。這時(shí)A已經(jīng)執(zhí)行完一個(gè)完整的任務(wù)珊膜,也就是說(shuō)username和userpassword這兩個(gè)實(shí)例變量已經(jīng)同時(shí)被賦值,不存在臟讀的基本環(huán)境宣脉。

3.synchronized鎖重入:關(guān)鍵字synchronized擁有鎖重入的功能车柠,也就是在使用synchronized時(shí),當(dāng)一個(gè)線程得到一個(gè)對(duì)象鎖后塑猖,再次請(qǐng)求此對(duì)象鎖時(shí)是可以再次得到該對(duì)象的鎖的竹祷。這也證明在一個(gè)synchronized方法/塊的內(nèi)部調(diào)用本類的其他synchronized方法/塊時(shí),是永遠(yuǎn)可以得到鎖的羊苟。
子類是完全可以通過(guò)“可重入鎖”調(diào)用父類的同步方法的塑陵。
“可重入鎖”的概念是:自己可以再次獲取自己的內(nèi)部鎖。比如有1條線程獲得了某個(gè)對(duì)象的鎖蜡励,此時(shí)這個(gè)對(duì)象鎖還沒(méi)有釋放令花,當(dāng)其再次想要獲取這個(gè)對(duì)象的鎖的時(shí)候還是可以獲取的阻桅,如果鎖不可重入的話就會(huì)造成死鎖。
(底層原理是JVM的管程來(lái)實(shí)現(xiàn)詳細(xì)可以看另一篇博客)

4.出現(xiàn)異常兼都,鎖自動(dòng)釋放:當(dāng)一個(gè)線程執(zhí)行代碼出現(xiàn)異常時(shí)嫂沉,其所持有的鎖會(huì)自動(dòng)釋放。(底層也是管程來(lái)實(shí)現(xiàn):如果一個(gè)同步方法執(zhí)行期間拋出了異常扮碧,并且在方法內(nèi)部無(wú)法處理此異常趟章,那么這個(gè)同步方法所持有的管程將在異常拋到同步方法之外時(shí)自動(dòng)釋放)。

5.同步不具有繼承性:父類方法有synchronized關(guān)鍵字慎王,子類重寫(xiě)該方法不帶synchronized關(guān)鍵字蚓土,那么子類該方法就不是同步。

6.synchronized方法的缺點(diǎn)及synchronized代碼塊的優(yōu)點(diǎn):synchronized關(guān)鍵字修飾方法的缺點(diǎn)就在于赖淤,當(dāng)該方法需要長(zhǎng)時(shí)間執(zhí)行時(shí)蜀漆,另一個(gè)線程需要等待持有線程執(zhí)行完才能允許。 synchronized修飾代碼塊就可以解決這個(gè)缺點(diǎn)漫蛔,當(dāng)兩個(gè)并發(fā)線程訪問(wèn)同一個(gè)對(duì)象object中的synchronized(this)同步代碼塊時(shí)嗜愈,一段時(shí)間內(nèi)只有一個(gè)線程被執(zhí)行,另一個(gè)線程必須等待當(dāng)前線程執(zhí)行完這個(gè)代碼后才能執(zhí)行該代碼莽龟。也就是說(shuō)在方法內(nèi)同步代碼塊外的部分可以異步執(zhí)行蠕嫁。那么將長(zhǎng)時(shí)間執(zhí)行的一部分代碼置于同步代碼塊外就能異步執(zhí)行這一耗時(shí)較長(zhǎng)的代碼塊,從而縮短運(yùn)行時(shí)間毯盈。

7.synchronized代碼塊:關(guān)于synchronized(this)需要注意的是剃毒,當(dāng)一個(gè)線程訪問(wèn)object的一個(gè)synchronized(this)同步代碼塊時(shí),其他線程對(duì)同一個(gè)object中所有其他synchronized(this)同步代碼塊的訪問(wèn)將被阻塞搂赋,這說(shuō)明synchronized使用的“對(duì)象監(jiān)視器”是一個(gè)赘阀。和synchronized方法一樣,synchronized(this)代碼也是鎖定當(dāng)前對(duì)象脑奠。

8.將任意對(duì)象作為對(duì)象監(jiān)視器:多個(gè)線程調(diào)用同一個(gè)對(duì)象中的不同名稱的synchronized同步方法或synchronized(this)同步代碼塊時(shí)基公,調(diào)用的效果就是按順序執(zhí)行,也就是同步的宋欺,阻塞的轰豆。
這說(shuō)明synchronized同步方法或synchronized(this)同步代碼塊分別有兩種作用
(1)synchronized同步方法
1)對(duì)其他synchronized同步方法或synchronized(this)同步代碼塊調(diào)用呈阻塞狀態(tài)。
2)同一時(shí)間只有一個(gè)線程可以執(zhí)行synchronized同步方法中的代碼齿诞。
(2)synchronized(this)同步代碼塊
1)對(duì)其他synchronized同步方法或synchronized(this)同步代碼塊調(diào)用呈阻塞狀態(tài)酸休。
2)同一時(shí)間只有一個(gè)線程可以執(zhí)行synchronized(this)同步方法中的代碼。

     synchronized(非this對(duì)象x)同步代碼塊
     1)在多個(gè)線程持有“對(duì)象監(jiān)視器”為同一對(duì)象的前提下祷杈,同一時(shí)間只有一個(gè)線程可以執(zhí)行synchronized(非this對(duì)象x)同步代碼塊斑司。
     2)當(dāng)持有“對(duì)象監(jiān)視器”為同一個(gè)對(duì)象的前提下,同一時(shí)間只有一個(gè)線程可以執(zhí)行synchronized(非this對(duì)象x)同步代碼塊中的代碼但汞。
    鎖非this對(duì)象具有一定的優(yōu)點(diǎn):如果在一個(gè)類中有很多個(gè)synchronized方法宿刮,這時(shí)雖然能實(shí)現(xiàn)同步互站,但會(huì)受到阻塞,所以影響運(yùn)行效率糙置;但如果使用同步代碼塊鎖非this對(duì)象云茸,則synchronized(非this)代碼塊中的程序與同步方法是異步的是目,不與其他鎖this同步方法爭(zhēng)搶this鎖谤饭,則可大大提高運(yùn)行效率。

9.下面是一個(gè)兩個(gè)synchronized同步方法之間的臟讀現(xiàn)象:main線程開(kāi)始執(zhí)行時(shí)Thread1先執(zhí)行并進(jìn)入if (list.getSize() < 1) 判斷語(yǔ)句中懊纳,并進(jìn)入睡眠揉抵,但是另一個(gè)線程可以異步也進(jìn)入if (list.getSize() < 1) 這個(gè)判斷語(yǔ)句中那么這兩個(gè)就都可以add(data)。

因?yàn)樗鼈兂钟械氖遣煌膶?duì)象監(jiān)視器:同步代碼塊放在非同步synchronized方法中進(jìn)行聲明嗤疯,并不能保證調(diào)用方法的線程的執(zhí)行同步/順序性冤今,也就是線程調(diào)用方法的順序是無(wú)序的,雖然在同步塊中執(zhí)行的順序是同步的茂缚,這樣極易出現(xiàn)臟讀問(wèn)題戏罢。

public class MyOneList{
    private List list = new ArrayList();
    synchronized public void add(String data){
        list.add(data);
    }
    synchronized public int getSize(){
        return list.size();
    }
}
public class MyService {
    public MyOneList addServiceMethod(MyOneList list,String data){
        try {
            if (list.getSize() < 1) {
                Thread.sleep(2000);
                list.add(data);
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        return list;
    }   
}
public class MyThread1 extends Thread{
    private MyOneList list;
    public MyThread1(MyOneList list){
        super();
        this.list = list;
    }
    @Override
    public void run(){
        MyService msRef = new MyService();
        msRef.addServiceMethod(list,"A");
    }
}
public class MyThread2 extends Thread{
    private MyOneList list;
    public MyThread2(MyOneList list){
        super();
        this.list = list;
    }
    @Override
    public void run(){
        MyService msRef = new MyService();
        msRef.addServiceMethod(list,"B");
    }
}
public class run{
    public static void main(String[] args) throws InterruptedException {
        MyOneList list = new MyOneList();
        MyThread1 thread1 = new MyThread1(list);
        thread1.setName("A");
        thread1.start();
        MyThread2 thread2 = new MyThread2(list);
        thread2.setName("B");
        thread2.start();
        Thread.sleep(6000);
        System.out.println("listSize=" + list.getSize());
    }
}

使用同步代碼塊可以避免上述臟讀現(xiàn)象,添加synchronized (list) 可以使兩個(gè)線程持有同一個(gè)對(duì)象監(jiān)視器:

public class MyService {
    public MyOneList addServiceMethod(MyOneList list,String data){
        try {
            synchronized (list) {
                if (list.getSize() < 1) {
                    Thread.sleep(2000);
                }
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        return list;
    }
}
 “synchronized (非list對(duì)象x) ”格式的寫(xiě)法是將x對(duì)象本身作為“對(duì)象監(jiān)視器”,這樣就可以得出以下3個(gè)結(jié)論:
1)當(dāng)多個(gè)線程同時(shí)執(zhí)行synchronized (x){}同步代碼塊時(shí)呈同步效果脚囊。
2)當(dāng)其他線程執(zhí)行x對(duì)象中synchronized同步方法時(shí)呈同步效果龟糕。
3)當(dāng)其他線程執(zhí)行x對(duì)象方法里面的synchronized (this)代碼塊時(shí)也是呈現(xiàn)同步效果。但需要注意:如果其他線程調(diào)用不加synchronized關(guān)鍵字的方法時(shí)悔耘,還是異步調(diào)用讲岁。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市衬以,隨后出現(xiàn)的幾起案子缓艳,更是在濱河造成了極大的恐慌,老刑警劉巖看峻,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阶淘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡互妓,警方通過(guò)查閱死者的電腦和手機(jī)溪窒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)车猬,“玉大人霉猛,你說(shuō)我怎么就攤上這事≈槿颍” “怎么了惜浅?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)伏嗜。 經(jīng)常有香客問(wèn)我坛悉,道長(zhǎng)伐厌,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任裸影,我火速辦了婚禮挣轨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘轩猩。我一直安慰自己卷扮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布均践。 她就那樣靜靜地躺著晤锹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪彤委。 梳的紋絲不亂的頭發(fā)上鞭铆,一...
    開(kāi)封第一講書(shū)人閱讀 49,842評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音焦影,去河邊找鬼车遂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛斯辰,可吹牛的內(nèi)容都是我干的舶担。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼椒涯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼柄沮!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起废岂,我...
    開(kāi)封第一講書(shū)人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤祖搓,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后湖苞,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體拯欧,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年财骨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了镐作。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡隆箩,死狀恐怖该贾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情捌臊,我是刑警寧澤杨蛋,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響逞力,放射性物質(zhì)發(fā)生泄漏曙寡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一寇荧、第九天 我趴在偏房一處隱蔽的房頂上張望举庶。 院中可真熱鬧,春花似錦揩抡、人聲如沸户侥。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)添祸。三九已至,卻和暖如春寻仗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背凡壤。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工署尤, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亚侠。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓曹体,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親硝烂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子箕别,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • Java8張圖 11、字符串不變性 12滞谢、equals()方法串稀、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,696評(píng)論 0 11
  • 一:java概述:1狮杨,JDK:Java Development Kit母截,java的開(kāi)發(fā)和運(yùn)行環(huán)境,java的開(kāi)發(fā)工...
    ZaneInTheSun閱讀 2,635評(píng)論 0 11
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法橄教,類相關(guān)的語(yǔ)法清寇,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法护蝶,異常的語(yǔ)法华烟,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,598評(píng)論 18 399
  • 本文出自 Eddy Wiki ,轉(zhuǎn)載請(qǐng)注明出處:http://eddy.wiki/interview-java.h...
    eddy_wiki閱讀 2,076評(píng)論 0 14
  • 1.解決信號(hào)量丟失和假喚醒 public class MyWaitNotify3{ MonitorObject m...
    Q羅閱讀 873評(píng)論 0 1