死锁现象
一、情景
我们先看一下出现死锁的情景:
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)); 会出现的结果:
两个线程在得到第一个锁的时候,在去拿第二个锁,没有拿到,就会释放了第一次拿到的那个锁,两个线程就这样的谦让,知道最后;
最后我们知道,有没有出现死锁,没有,只是在一直谦让;