死锁现象

it2022-05-09  30

一、情景

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

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

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

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

 

转载于:https://www.cnblogs.com/lys-lyy/p/11108273.html


最新回复(0)