死锁现象

一、情景

我们先看一下出现死锁的情景:

package com.youyou.ch7;

import com.youyou.tools.SleepTools;

public class NormalDeadLock {
    private static Object valueFirst = new Object();//第一个锁
    private static Object valueSecond = new Object();//第二个锁

    //先拿第一个锁,再拿第二个锁
    private static void fisrtToSecond() throws InterruptedException {
        String name = Thread.currentThread().getName();
        synchronized (valueFirst){
            System.out.println("method fisrtToSecond ,Thread :" + name+" get first");
            SleepTools.ms(1000);
            synchronized (valueSecond){
                System.out.println("method fisrtToSecond ,Thread :" + name+" get second");
            }
        }
    }

    //先拿第二个锁,再拿第一个锁
    private static void SecondToFisrt() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        synchronized (valueSecond) {
            System.out.println("method SecondToFisrt ,Thread :" + threadName+" get first");
            SleepTools.ms(1000);
            synchronized (valueFirst) {
                System.out.println("method SecondToFisrt ,Thread :" + threadName+" get second");
            }
        }
    }

    private static class TestThread extends Thread{
        private String name;
        public TestThread(String name){this.name = name;}
        @Override
        public void run(){
            Thread.currentThread().setName(name);
            try {
                SecondToFisrt();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Thread.currentThread().setName("TestDeadLock");
        TestThread testThread = new TestThread("SubTestThread");
        testThread.start();
        try {
            fisrtToSecond();//先拿第一个锁,再拿第二个锁
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
View Code

那么我们看出来,出现死锁的情况是:

资源一定是多于1个,同时小于等于竞争的线程数,资源只有一个,只会产生激烈的竞争。

死锁的根本成因:获取锁的顺序不一致导致。

所以锁的顺序要一致;

但是一致的时候,也不一定会是不出现的,下面我们看下面的例子;

二、查看死锁的方式

cd jdk目录

jps -m

jstack 线程号

查看结果:

三、死锁的第二种情况

直接上代码

public class PayCompany {

    /*执行转账动作的线程*/
    private static class TransferThread extends Thread{

        private String name;//线程名字
        private UserAccount from; 
        private UserAccount to; 
        private int amount;
        private ITransfer transfer; //实际的转账动作

        public TransferThread(String name, UserAccount from, UserAccount to,
                              int amount, ITransfer transfer) {
            this.name = name;
            this.from = from;
            this.to = to;
            this.amount = amount;
            this.transfer = transfer;
        }


        public void run(){
            Thread.currentThread().setName(name);
            try {
                transfer.transfer(from,to,amount);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        PayCompany payCompany = new PayCompany();
        UserAccount zhangsan = new UserAccount("zhangsan",20000);
        UserAccount lisi = new UserAccount("lisi",20000);
        ITransfer transfer = new SafeOperateToo();
        TransferThread zhangsanToLisi = new TransferThread("zhangsanToLisi"
                ,zhangsan,lisi,2000,transfer);
        TransferThread lisiToZhangsan = new TransferThread("lisiToZhangsan"
                ,lisi,zhangsan,4000,transfer);
        zhangsanToLisi.start();
        lisiToZhangsan.start();
    }
}

public class UserAccount {
    //private int id;
    private final String name;//账户名称
    private int money;//账户余额

    //显示锁
 //   private final Lock lock = new ReentrantLock();

  //  public Lock getLock() {
  //      return lock;
  //  }

    public UserAccount(String name, int amount) {
        this.name = name;
        this.money = amount;
    }

    public String getName() {
        return name;
    }

    public int getAmount() {
        return money;
    }

    @Override
    public String toString() {
        return "UserAccount{" +
                "name='" + name + '\'' +
                ", money=" + money +
                '}';
    }

    //转入资金
    public void addMoney(int amount){
        money = money + amount;
    }

    //转出资金
    public void flyMoney(int amount){
        money = money - amount;
    }
}

public class TrasnferAccount implements ITransfer {
    
    @Override
    public void transfer(UserAccount from, UserAccount to, int amount) 
            throws InterruptedException {
        synchronized (from){//先锁转出
            System.out.println(Thread.currentThread().getName()
                    +" get"+from.getName());
            Thread.sleep(100);
            synchronized (to){//再锁转入
                System.out.println(Thread.currentThread().getName()
                        +" get"+to.getName());
                from.flyMoney(amount);
                to.addMoney(amount);
            }
        }
    }
}
public interface ITransfer {
    /**
     * 
     * @param from 转出账户
     * @param to 转入账户
     * @param amount 转账金额
     * @throws InterruptedException
     */
    void transfer(UserAccount from, UserAccount to, int amount) 
            throws InterruptedException;
}
View Code

这样的话,就会产生死锁,虽然说是锁是一致的顺序的

所以,解决的方法有两种:

1.

public class SafeOperate implements ITransfer {
    private static Object tieLock = new Object();//加时赛锁

    @Override
    public void transfer(UserAccount from, UserAccount to, int amount)
            throws InterruptedException {
        
        int fromHash = System.identityHashCode(from);
        int toHash = System.identityHashCode(to);
        //先锁hash小的那个
        if(fromHash<toHash) {
            synchronized (from){
                System.out.println(Thread.currentThread().getName()
                        +" get"+from.getName());
                Thread.sleep(100);
                synchronized (to){
                    System.out.println(Thread.currentThread().getName()
                            +" get"+to.getName());
                    from.flyMoney(amount);
                    to.addMoney(amount);
                }
            }            
        }else if(toHash<fromHash) {
            synchronized (to){
                System.out.println(Thread.currentThread().getName()
                        +" get"+to.getName());
                Thread.sleep(100);
                synchronized (from){
                    System.out.println(Thread.currentThread().getName()
                            +" get"+from.getName());
                    from.flyMoney(amount);
                    to.addMoney(amount);
                }
            }            
        }else {//解决hash冲突的方法
            synchronized (tieLock) {
                synchronized (from) {
                    synchronized (to) {
                        from.flyMoney(amount);
                        to.addMoney(amount);                        
                    }
                }
            }
        }
        
    }
}
View Code

2.

public class UserAccount {
    //private int id;
    private final String name;//账户名称
    private int money;//账户余额

    //显示锁
   private final Lock lock = new ReentrantLock();

   public Lock getLock() {
       return lock;
  }

    public UserAccount(String name, int amount) {
        this.name = name;
        this.money = amount;
    }

    public String getName() {
        return name;
    }

    public int getAmount() {
        return money;
    }

    @Override
    public String toString() {
        return "UserAccount{" +
                "name='" + name + '\'' +
                ", money=" + money +
                '}';
    }

    //转入资金
    public void addMoney(int amount){
        money = money + amount;
    }

    //转出资金
    public void flyMoney(int amount){
        money = money - amount;
    }
}
View Code
public class SafeOperateToo implements ITransfer {

    @Override
    public void transfer(UserAccount from, UserAccount to, int amount)
            throws InterruptedException {
        Random r = new Random();
        while(true) {
            if(from.getLock().tryLock()) {
                try {
                    System.out.println(Thread.currentThread().getName()
                            +" get "+from.getName());
                    if(to.getLock().tryLock()) {
                        try {
                            System.out.println(Thread.currentThread().getName()
                                    +" get "+to.getName());                            
                            //两把锁都拿到了
                            from.flyMoney(amount);
                            to.addMoney(amount);
                            break;
                        }finally {
                            to.getLock().unlock();
                        }
                    }
                }finally {
                    from.getLock().unlock();
                }
            }
            SleepTools.ms(r.nextInt(10));
        }
    }
}
View Code

 这里我们有一个需要注意的是:(活锁的出现)

假如说在 类 SafeOperateToo 代码里面,没有休眠时间:SleepTools.ms(r.nextInt(10)); 会出现的结果:

两个线程在得到第一个锁的时候,在去拿第二个锁,没有拿到,就会释放了第一次拿到的那个锁,两个线程就这样的谦让,知道最后;

最后我们知道,有没有出现死锁,没有,只是在一直谦让;

 

posted @ 2019-06-29 23:19  宥宥美美  阅读(465)  评论(0编辑  收藏  举报