java并發(fā)

java并發(fā)

1. 線程的通信

wait() 使線程進(jìn)入睡眠狀態(tài)
notify() 隨機(jī)喚醒一個等待的線程,它將獲得一次搶奪鎖的機(jī)會
notifyAll() 喚醒所有等待的線程
臨界區(qū): 需要線程同步的代碼區(qū)
$$
運(yùn)行狀態(tài)
\dfrac{ wait() }{}
睡眠狀態(tài)
\dfrac{ notify() }{}
等鎖狀態(tài)
\dfrac{ 獲得鎖 }{}
運(yùn)行狀態(tài)
$$

注意

  • 3個方法只能在同步塊里面被調(diào)用
  • 使用while避免被假喚醒
  • 不要使用全局對象,字符串常量作為鎖
    public class MywaitNotify{
        MonitorObject monitor = new MonitorObject();
        boolean wasSignalled = false;
        
        public void doWait() {
            synchronized(monitor){
                while (!wasSignalled){  //用while代替if防止假喚醒
                    monitor.wait();  //wait(),notify(),notifyAll()必須位于同步塊內(nèi)
                }
                wasSignalled = false;
            }
        }
        
        public void doNotify() {
            synchronized(monitor){
                wasSignalled = true;
                monitor.notify();  //notity后并不意味著等待線程立即可以繼續(xù)執(zhí)行,而只是可以有機(jī)會再次搶奪鎖
            }
        }
    }

2. 饑餓與公平

  • 若一個線程因為cpu時間全部被其他線程搶走而得不到cpu運(yùn)行時間,這種狀態(tài)稱之為饑餓
  • 公平鎖的實現(xiàn)
public class FairLock {
    private boolean locked; //鎖區(qū)是否正處于鎖定狀態(tài)(鎖區(qū)是否已有其他線程存在尚未離開)
    private Thread lockingThread; //當(dāng)前在鎖區(qū)的那個線程
    private List<LockObject> waittingThreads = new ArrayList<LockObject>();
    
    
    public void lock() throws InterruptedException{
        LockObject lockObject = new LockObject();
        boolean isAllowedEnterLockedArea = false;
        
        //線程開始排隊,誰先進(jìn)來,誰先被add進(jìn)List,也就是排在最前面
        synchronized (this) {
            waittingThreads.add(lockObject);
        }
        
        //若不允許進(jìn)入鎖區(qū),則自旋等待
        while (!isAllowedEnterLockedArea) {
            synchronized (this) {
                /* 當(dāng)前線程是否允許進(jìn)入鎖區(qū)的線程(isAllowedEnterLockedArea)?
                 * 如果鎖區(qū)沒有其他線程(locked=false) 且 排隊排在最前面 
                 * 則當(dāng)前線程是被允許進(jìn)入鎖區(qū)的線程
                 */
                isAllowedEnterLockedArea = !locked && waittingThreads.get(0) == lockObject;
                
                /* 若允許進(jìn)入鎖區(qū),則在進(jìn)入前將"鎖區(qū)已有線程"的標(biāo)志置為true,
                 * 并從排隊隊列中將自己移除 */
                if (isAllowedEnterLockedArea) {
                    locked = true;
                    waittingThreads.remove(lockObject);
                    lockingThread = Thread.currentThread();
                    return;
                }
            }
            
            try {
                /* 不被允許進(jìn)入鎖區(qū)的線程(isAllowedEnterLockedArea=false)
                 * 使用他排隊時使用的lockObject對象作為鎖標(biāo)志的鎖睡眠了,
                 * 直到有其他線程調(diào)用這個lockObject對象的notify()叫醒他
                 * -----------------------
                 * 這里為什么不直接調(diào)用lockObject.wait()方法?
                 * --為了防止信號丟失!
                 * 因為自帶的wait()或notify()方法是沒有狀態(tài)的, 
                 * 有可能當(dāng)前線程(線程A)執(zhí)行到此處,在還沒有進(jìn)入睡眠時, 
                 * 其他線程unlock,就已經(jīng)notify了線程A, 
                 * 這時由于線程A不是出于睡眠狀態(tài)而忽略了notify的信號,
                 * 接著線程A進(jìn)入睡眠, 因為喚醒它的信號已被忽略,
                 * 那么線程A將永遠(yuǎn)不會醒來
                 */
                lockObject.doWait();  
            } catch (InterruptedException e) {
                /* 若因出現(xiàn)異常,在等待進(jìn)入鎖區(qū)的睡眠中被異常打斷,
                 * 則將其從排隊隊列中踢出 */
                synchronized (this) {
                    waittingThreads.remove(lockObject);
                }
                throw e;
            }
        }
    }
    
    
    public synchronized void unlock(){
        if (this.lockingThread != Thread.currentThread()){
            throw new IllegalMonitorStateException("當(dāng)前線程并不是加鎖者,解鎖失敗");
        }
        locked = false;
        lockingThread = null;
        //在離開鎖區(qū)時,若還有其他線程在排隊等待進(jìn)入鎖區(qū),則叫醒排在最前面的那個線程
        if (waittingThreads.size() > 0){
            waittingThreads.get(0).doNotify();
        }
    }
}
public class LockObject {
    private boolean isNotified;  //使用成員變量記住信號,防止信號丟失(帶狀態(tài)的鎖)
    
    public synchronized void doWait() throws InterruptedException{
        while(!isNotified){
            this.wait();
        }
        isNotified = false;
    }
    
    public synchronized void doNotify(){
        this.isNotified = true;
        this.notify();
    }
    
    @Override
    public boolean equals(Object o){
        return this == o;
    }
}

3. 嵌套管程鎖死

有嵌套同步塊A和B,鎖分別是LockA,LockB,線程1在持有外層同步塊A的鎖LockA的情況下,進(jìn)入同步塊B,調(diào)用LockB.wait()進(jìn)入睡眠,等待其他線程調(diào)用LockB.notify()將其喚醒, 至此,線程1就釋放了LockB, 但仍持有LockA. 這時, 其他線程想進(jìn)入鎖同樣為LockA的同步塊去喚醒線程A(調(diào)用LockB.notify),卻發(fā)現(xiàn)因線程1持有鎖LockA睡眠了,無法獲得該鎖,從而導(dǎo)致死鎖狀態(tài)
簡要的歸納就是:

    線程帶著其它鎖沒有釋放的情況下進(jìn)入了休眠(因同步塊嵌套導(dǎo)致),導(dǎo)致其他線程無法獲得這個鎖去叫醒他

嵌套管程鎖死的例子:

public class Lock{
    private Monitor monitor = new Monitor();
    private boolean locked;
    
    public void lock(){
        synchronized (this){
            while (locked){
                synchronized(monitor){
                    //進(jìn)入休眠時,只釋放了monitor鎖,沒有釋放this鎖
                    monitor.wait(); 
                }
            }
            locked = true;
        }
    }
    
    public void unlock(){
        //因為this被占用, 無法進(jìn)入同步塊喚醒已睡眠的線程
        synchronized(this){
            locked = false;
            synchronized(monitor){
                monitor.notify();
            }
        }
    }
}

死鎖嵌套管程鎖死 的區(qū)別:

  死鎖中,兩個線程都在等待對方釋放鎖. 
  嵌套管程鎖死中,線程1持有鎖A, 同事等待從線程2發(fā)來的信號,線程2需要鎖A來發(fā)信號給線程1.

4. Slipped Conditions

從一個線程檢查某一特定條件, 到操作此條件期間,這個條件已被其他線程所改變

public class Lock {
    private boolean locked;
    
    public void lock() {
        synchronized (this){  //同步塊1
            while (locked){
                this.wait();
            }
        }
        
        /* 這里同步塊1與同步塊2間可能出現(xiàn)slipped conditions:
         * 一個線程(線程A)檢查到locked=false跳出同步塊1后,即將準(zhǔn)備進(jìn)入同步塊2,此時:
         * this鎖已被釋放. 這時新的線程B調(diào)用lock()進(jìn)入同步塊1,檢查到locked=false,
         * 因此線程B馬上跳出同步塊1,進(jìn)入同步塊2,將locked置為true,
         * 接下來線程A才進(jìn)入同步塊2,而此時發(fā)現(xiàn):期間locked已被別人(線程B)修改過了
         */
        synchronized(this){  //同步塊2
            locked = true;
        }
    }
    
    public void unlocked() {
        locked = false;
        this.notify();
    }
}

為避免slipped conditions, 條件的檢查和設(shè)置必須是原子的, 也就是在第一個線程檢查和設(shè)置條件期間,不會有其他線程檢查這個條件

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诫肠,一起剝皮案震驚了整個濱河市涛浙,隨后出現(xiàn)的幾起案子丈秩,更是在濱河造成了極大的恐慌镀裤,老刑警劉巖梦碗,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喘沿,死亡現(xiàn)場離奇詭異,居然都是意外死亡猾骡,警方通過查閱死者的電腦和手機(jī)瑞躺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來兴想,“玉大人幢哨,你說我怎么就攤上這事∩┍悖” “怎么了捞镰?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長顽悼。 經(jīng)常有香客問我曼振,道長,這世上最難降的妖魔是什么蔚龙? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任冰评,我火速辦了婚禮,結(jié)果婚禮上木羹,老公的妹妹穿的比我還像新娘甲雅。我一直安慰自己,他們只是感情好坑填,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布抛人。 她就那樣靜靜地躺著,像睡著了一般脐瑰。 火紅的嫁衣襯著肌膚如雪妖枚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天苍在,我揣著相機(jī)與錄音绝页,去河邊找鬼荠商。 笑死,一個胖子當(dāng)著我的面吹牛续誉,可吹牛的內(nèi)容都是我干的莱没。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼酷鸦,長吁一口氣:“原來是場噩夢啊……” “哼饰躲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起臼隔,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤嘹裂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后摔握,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體焦蘑,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年盒发,在試婚紗的時候發(fā)現(xiàn)自己被綠了例嘱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡宁舰,死狀恐怖拼卵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蛮艰,我是刑警寧澤腋腮,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站壤蚜,受9級特大地震影響即寡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜袜刷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一聪富、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧著蟹,春花似錦墩蔓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至涮雷,卻和暖如春阵面,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工样刷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗽交,地道東北人。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓颂斜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親拾枣。 傳聞我的和親對象是個殘疾皇子沃疮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評論 2 355

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