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

简单顺序死锁

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

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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值