并发编程实战死锁读书笔记之吐槽

来源:互联网 发布:软件行业的进项 编辑:程序博客网 时间:2024/05/16 08:34

简单顺序死锁

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 {    }}

是不是看完就很好理解为什么在大多数系统下会很快发生死锁了?好坑好坑好坑,一声不说就直接用了个错误的类,以上也说明在附有源码的时候,看书一定要对照源码看,这样能避免很多迷惑性的问题,写此博客也希望能帮到很多跟我一样有困惑的人【我百度,谷歌了很久始终没有找到有人写过这个】

原创粉丝点击