併發編程實戰死鎖讀書筆記之吐槽

簡單順序死鎖

package com.txr.TransferMoneyDemo;

/**
 * Created by txr on 2017/7/28.
 */
public class LeftRightDeadlock {
    private final Object left =new Object();
    private final Object right=new Object();
    public void leftRight(){
        synchronized (left){
            synchronized (right){
                dosomething();
            }
        }
    }
    public void rigthLeft(){
        synchronized (right){
            synchronized (left){
                dosomethingElse();
            }
        }
    }
}

顯然這樣會出現死鎖,比如A-->B   B--->A,這個時候第一個線程獲得了鎖A等待獲取鎖B,第二個線程線程獲取了鎖B等待獲取鎖A,這樣就形成了死鎖

動態的鎖順序死鎖

package com.txr.TransferMoneyDemo;

import javax.naming.InsufficientResourcesException;

/**
 * Created by zj-db0236 on 2017/7/28.
 */
public class LeftRightDeadlock {
    public void transferMoney(Account fromAcct,Account toAcct,DollarAmout amout) throws InsufficientResourcesException {
        synchronized (fromAcct){
            synchronized (toAcct){
                if(fromAcct.getBalance().compareTo(amout)<0)
                    throw new InsufficientResourcesException();
                else{
                    fromAcct.debit(amout);
                    toAcct.credit(amout);
                }
            }
        }
    }
}

這種死鎖也很容易理解,比如 A--->B   另一個線程  B--->A
A:transferMoney(mycount ,yourAccount,1000)
B:transferMoney(yourcount ,myAccount,2000)
和上面一樣會發生死鎖

通過鎖順序來避免死鎖

 private static final Object tieLock =new Object();

    /**
     * 以順序鎖的形式來轉錢,System.identityHashCode是個本地方法,
     * 是以引用地址來計算的,HashCode是以值來計算的
     * @param fromAcct 轉賬人
     * @param toAcct   被轉賬人
     * @param amout     錢
     * @throws InsufficientResourcesException
     */
    public static void transferMoney(final Account fromAcct,
                              final Account toAcct, final DollarAmout amout) throws InsufficientResourcesException {
        class Helper{
            public void transfer() throws InsufficientResourcesException{
                    //轉賬人出賬
                    fromAcct.debit(amout);
                    //被轉賬人入賬
                    toAcct.credit(amout);

            }
        }
        int fromHash =System.identityHashCode(fromAcct);
        int toHash = System.identityHashCode(toAcct);

        if(fromHash<toHash){
            synchronized (fromAcct){
                synchronized (toAcct){
                    new Helper().transfer();
                }
            }
        }else if(fromHash>toHash) {
            synchronized (toAcct) {
                synchronized (fromAcct) {
                    new Helper().transfer();
                }
            }
        }else{
            synchronized (tieLock){
                synchronized (fromAcct){
                    synchronized (toAcct){
                        new Helper().transfer();
                    }
                }
            }
        }
    }

以下來坑了,書上說如下代碼典型情況下會發生死鎖

public class IdentityHashCodeDemo {
    private static final int NUM_THREADS=20;
    private static final int NUM_ACCOUNTS=5;
    private static final int NUM_ITERATIONS=100000;

    /**
     * 模擬十萬次轉賬居然會發生死鎖這是爲什麼?
     * @param args
     */
    public static void main(String[] args) {
        final Random rnd=new Random();
        final Account[] accounts=new Account[NUM_ACCOUNTS];

        for (int i=0;i<accounts.length;i++)
            accounts[i]=new Account();
        //模擬十萬次轉賬,不考慮賬戶爲負數的情況
        class TransferThread extends Thread{
            @Override
            public void run() {
                for(int i=0;i<NUM_ITERATIONS;i++){

                    int fromAcct=rnd.nextInt(NUM_ACCOUNTS);
                    int toAcct=rnd.nextInt(NUM_ACCOUNTS);
                    DollarAmout amout=new DollarAmout(rnd.nextInt(1000));
                    try {
                        //很納悶明明用了順序鎖爲什麼還會很快出現死鎖
                        transferMoney(accounts[fromAcct],accounts[toAcct],amout);
                    } catch (InsufficientResourcesException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        for (int i=0;i<NUM_THREADS;i++)
            new TransferThread().start();
    }
}

百思不得其解,最後沒辦法上網把源碼當下來了,查看源碼傻眼了,源碼如是寫着
package net.jcip.examples;

import java.util.*;

import net.jcip.examples.DynamicOrderDeadlock.Account;
import net.jcip.examples.DynamicOrderDeadlock.DollarAmount;

/**
 * DemonstrateDeadlock
 * <p/>
 * Driver loop that induces deadlock under typical conditions
 *
 * @author Brian Goetz and Tim Peierls
 */
public class DemonstrateDeadlock {
    private static final int NUM_THREADS = 20;
    private static final int NUM_ACCOUNTS = 5;
    private static final int NUM_ITERATIONS = 1000000;

    public static void main(String[] args) {
        final Random rnd = new Random();
        final Account[] accounts = new Account[NUM_ACCOUNTS];

        for (int i = 0; i < accounts.length; i++)
            accounts[i] = new Account();

        class TransferThread extends Thread {
            public void run() {
                for (int i = 0; i < NUM_ITERATIONS; i++) {
                    int fromAcct = rnd.nextInt(NUM_ACCOUNTS);
                    int toAcct = rnd.nextInt(NUM_ACCOUNTS);
                    DollarAmount amount = new DollarAmount(rnd.nextInt(1000));
                    try {
                        DynamicOrderDeadlock.transferMoney(accounts[fromAcct], accounts[toAcct], amount);
                    } catch (DynamicOrderDeadlock.InsufficientFundsException ignored) {
                    }
                }
            }
        }
        for (int i = 0; i < NUM_THREADS; i++)
            new TransferThread().start();
    }
}
看到了什麼?擔心你們粗心我給勾出來了



簡直就苦笑不得,書上壓根就沒有提到這個類,他就默默的用了也不說,然後我們再來看看DynamicOrderDeadlock類
package net.jcip.examples;

import java.util.concurrent.atomic.*;

/**
 * DynamicOrderDeadlock
 * <p/>
 * Dynamic lock-ordering deadlock
 *
 * @author Brian Goetz and Tim Peierls
 */
public class DynamicOrderDeadlock {
    // Warning: deadlock-prone!
    public static void transferMoney(Account fromAccount,
                                     Account toAccount,
                                     DollarAmount amount)
            throws InsufficientFundsException {
        synchronized (fromAccount) {
            synchronized (toAccount) {
                if (fromAccount.getBalance().compareTo(amount) < 0)
                    throw new InsufficientFundsException();
                else {
                    fromAccount.debit(amount);
                    toAccount.credit(amount);
                }
            }
        }
    }

    static class DollarAmount implements Comparable<DollarAmount> {
        // Needs implementation

        public DollarAmount(int amount) {
        }

        public DollarAmount add(DollarAmount d) {
            return null;
        }

        public DollarAmount subtract(DollarAmount d) {
            return null;
        }

        public int compareTo(DollarAmount dollarAmount) {
            return 0;
        }
    }

    static class Account {
        private DollarAmount balance;
        private final int acctNo;
        private static final AtomicInteger sequence = new AtomicInteger();

        public Account() {
            acctNo = sequence.incrementAndGet();
        }

        void debit(DollarAmount d) {
            balance = balance.subtract(d);
        }

        void credit(DollarAmount d) {
            balance = balance.add(d);
        }

        DollarAmount getBalance() {
            return balance;
        }

        int getAcctNo() {
            return acctNo;
        }
    }

    static class InsufficientFundsException extends Exception {
    }
}

是不是看完就很好理解爲什麼在大多數系統下會很快發生死鎖了?好坑好坑好坑,一聲不說就直接用了個錯誤的類,以上也說明在附有源碼的時候,看書一定要對照源碼看,這樣能避免很多迷惑性的問題,寫此博客也希望能幫到很多跟我一樣有困惑的人【我百度,谷歌了很久始終沒有找到有人寫過這個】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章