各種死鎖,性能和測試

加一堆鎖也許能保證線程安全, 但是卻可能造成死鎖

數(shù)據(jù)庫服務(wù)器避免死鎖的方式
  1. 發(fā)現(xiàn)死鎖;
  2. 選擇一個(gè)犧牲者, 退出事務(wù);
  3. 程序繼續(xù)運(yùn)行.
jvm解決死鎖的方式
  1. 這些線程直接不能用....恢復(fù)的唯一方式是終止并重啟, 然后寄希望于不要再發(fā)生同樣的事.

鎖順序死鎖

如果所有線程通過固定順序獲取多個(gè)鎖, 程序就不會(huì)出現(xiàn)鎖順序死鎖

//警告: 易發(fā)生死鎖
public class LeftRightDeadLock{
    private final Object left = new Object();
    private final Object right = new Object();
    
    public void leftRight(){
        synchronized(left){
                sychronized(right){
                    //do some thing
                }
        }
    }
    public void rightLeft(){
        synchronized(right){
                sychronized(left){
                    //do some thing
                }
        }
    }
}
LeftRightDeadLock的偶發(fā)時(shí)序

下面的順序死鎖不那么一目了然

//警告:易產(chǎn)生死鎖
public void transferMoney(Account fromAccount,Account toAccount,DollarAmount amount) throws InsufficientFundsException {
    synchronized(fromAccount){    //同時(shí)向?qū)Ψ睫D(zhuǎn)賬時(shí), from 和 to兩個(gè)賬戶存在死鎖風(fēng)險(xiǎn)
        synchronized(toAccount){
             if(fromAccount.getBalance().compareTo(amount)<0)
                  throw new InsufficientFundsException ();
            else{
               fromAccount.debit(amount);
               toAccount.credit(amount);
            }
        }
    }
}

解決方法

制定鎖的順序 并且整個(gè)應(yīng)用中,獲取鎖必須遵守這個(gè)順序

下面用對(duì)象hashcode決定鎖順序, (hash沖突仍然存在死鎖風(fēng)險(xiǎn), 此時(shí)用競爭機(jī)制保證鎖定)

private static final Object tieLock = new Object();//"加時(shí)賽"鎖, 誰先拿到,誰就先執(zhí)行

public void transferMoney(Account fromAccount,Account toAccount,DollarAmount amount) throws InsufficientFundsException {
    class Helper{
        public void transfer() throws InsufficientFundsException {
             if(fromAccount.getBalance().compareTo(amount)<0)
                  throw new InsufficientFundsException ();
            else{
               fromAccount.debit(amount);
               toAccount.credit(amount);
        }
    }
    int fromHash = System.identityHashCode(fromAccount)
    int toHash = System.identityHashCode(toAccount)
    if(fromHash < toHash){//如果from的hashcode小, 則先鎖from
        synchronized(fromAccount){    
            synchronized(toAccount){
                 new Helper().transfer();
            }
        }
    }else if(fromHash > toHash){//如果to的hashcode小, 則先鎖to對(duì)象
        synchronized(toAccount){    
            synchronized(fromAccount){
                 new Helper().transfer();
            }
        }
    }else{ //如果兩個(gè)對(duì)象hash相同, 則鎖住整個(gè)轉(zhuǎn)賬對(duì)象. 一次只讓一個(gè)人轉(zhuǎn)
        synchronized(tieLock){
            synchronized(fromAccount){    
                synchronized(toAccount){
                     new Helper().transfer();
                }
            }
        }
    }
}

加時(shí)賽鎖有代替方案: 如果賬號(hào)有唯一屬性, 比如賬號(hào)id是唯一的, 那么直接比較賬號(hào)id即可. 這樣可以節(jié)省加時(shí)賽鎖


協(xié)作對(duì)象間的死鎖
//警告: 可能產(chǎn)生死鎖
class Taxi {
    @GuardedBy("this)
    private Point location,destination;
    private final Dispatcher dispatcher;
    
    public Taxi(Dispatcher dispatcher){
        this.dispatcher
    }
}

總結(jié)一下常見的多線程危險(xiǎn)
  1. 死鎖
  2. 饑餓

一個(gè)線程占著資源, 其他線程很久都等不到鎖

  1. 丟失信號(hào)

????就是在等鎖的時(shí)候,別人放開鎖的消息丟了,,,導(dǎo)致線程一直等下去了...

  1. 活鎖

????線程沒死, 但是拿不到資源, 就不停的重試,再重試....卡著無法前進(jìn)...解決辦法是做一些隨機(jī), 相互避讓.

java 線程api的優(yōu)先級(jí)僅僅是一種參考, 具體級(jí)別取決于系統(tǒng)的優(yōu)先級(jí).
另外又容易導(dǎo)致某個(gè)線程優(yōu)先級(jí)永遠(yuǎn)高于其他線程而導(dǎo)致饑餓

所以: 盡量不要去改變線程優(yōu)先級(jí)

一旦開始改變線程優(yōu)先級(jí), 則程序的行為就變成平臺(tái)相關(guān)了.并且會(huì)引入饑餓的風(fēng)險(xiǎn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容