死锁现象
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();
那么我们看出来,出现死锁的情况是:
资源一定是多于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;
这样的话,就会产生死锁,虽然说是锁是一致的顺序的
所以,解决的方法有两种:
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);
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;
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));
这里我们有一个需要注意的是:(活锁的出现)
假如说在 类 SafeOperateToo 代码里面,没有休眠时间:SleepTools.ms(r.nextInt(10)); 会出现的结果:
两个线程在得到第一个锁的时候,在去拿第二个锁,没有拿到,就会释放了第一次拿到的那个锁,两个线程就这样的谦让,知道最后;
最后我们知道,有没有出现死锁,没有,只是在一直谦让;
以上是关于死锁现象的主要内容,如果未能解决你的问题,请参考以下文章