死锁现象

Posted lys-lyy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了死锁现象相关的知识,希望对你有一定的参考价值。

一、情景

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

技术图片
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)); 会出现的结果:

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

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

技术图片

 

以上是关于死锁现象的主要内容,如果未能解决你的问题,请参考以下文章

2-7 死锁现象和递归锁

python 并发编程 多线程 死锁现象与递归锁

Linux同步机制 - 基本概念(死锁,活锁,饿死,优先级反转,护航现象)

死锁现象

死锁现象

同步函数死锁现象