精华内容
下载资源
问答
  • 文章目录引言什么是锁顺序死锁动态的锁顺序死锁实践启示录 引言 初次接触死锁的概念是大学的一门课程《操作系统原理》中描述的“哲学家进餐”问题。操作系统中,由于各个进程共享系统资源而可能出现死锁问题。同样 ...

    引言

    初次接触 “死锁” ,在大学《操作系统原理》课程上,笔者对经典的 “哲学家进餐” 问题至今印象深刻,当时想象一些人围成一个圈等着拿筷子的场景,还觉得挺好笑的。回到本文,什么是死锁呢?这个问题相比大家都不陌生,摘录 “百度百科” 的解释是这样的:

    死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

    操作系统中,锁是进程间通信的重要技术手段,它能解决进程间因共享资源可能出现的错误。如果使用不当或者过度使用,就可能会发生 “死锁” 问题 ,比如“哲学家饿死”这种 。

    Java 应用发生死锁时,程序可能就此阻塞,程序僵而不死,服务无法正常使用,这就是死锁最直接的后果。

    锁顺序死锁概述

    死锁产生的原因很多,锁顺序就是一种。它指一个线程同时需要多把锁时,由于锁的请求顺序不同,而导致的死锁问题。一起来看一下《Java 并发编程实践》一书中,锁顺序死锁的案例。

    案例回顾

    LeftRightDeadLock 类定义了两个锁成员变量和两个方法,分别以相反的顺序请求两把锁:

    public class LeftRightDeadLock {
    	private final Object left = new Object();
    	private final Object right = new Object();
    	
    	public void leftRight(){
    		synchronized (left) {
    			synchronized (right) {
    				//doSomethoing();
    			}
    		}
    	}
    	
    	public void rightLeft(){
    		synchronized (right) {
    			synchronized (left) {
    				//doSomethoing();
    			}
    		}
    	}
    }
    

    死锁分析

    死锁风险分析:当一个线程调用 leftRight 方法获取了 left 锁后,另一个线程同时调用了 rightLeft 方法获取了 right 锁。此时他们都会因等待另一个锁而阻塞,程序僵死。

    两个线程的操作交错进行时,对某一锁的请求顺序也是交错的,就容易因锁顺序而引发死锁问题。

    破解方法:如果方法需要同时请求多把锁,应该统一这些锁的请求顺序,保证顺序一致。

    动态的锁顺序死锁

    上述加锁是对针对固定成员变量的,控制加锁顺序比较容易。还一种场景,在方法中以参数作为请求锁,参数不同、锁也不同,顺序也无法预测,这就是动态的锁顺序死锁的诱因。

    《Java 并发编程实践》一书中给出的代码片段,它描述的场景是将资金从一个账户转入另一个账户,在开始转账之前,先要同时获取这两个账户对象的锁,确保通过原子操作来更新两个账户中的余额。

    抽象测试类

    抽象一个代表金额的 Amount 类,一个 Account 账户类,一个转账方法,设计类图。
    在这里插入图片描述

    编写实现代码

    按该类图结构编写代码如下:

    /**
     * 顶层金额抽象类:包含币种和余额两个属性
     * @author bh
     */
    public abstract class Amount implements Comparable<Amount>{
    	public abstract BigDecimal getBalance();
    	public abstract void setBalance(BigDecimal balance);
    	public abstract Currency getCurrency();
    	
    	@Override
    	public int compareTo(Amount o) {
    		if(o==null){
    			throw new NullPointerException("null arg.");
    		}
    		
    		if(this.getBalance()==null||o.getBalance()==null){
    			throw new NullPointerException("null arg.");
    		}
    		
    		return this.getBalance().compareTo(o.getBalance());
    	}
    }
    

    子类 DallarAmount代码:

    /**
     * 美元类
     * @author bh
     */
    public class DollarAmount extends Amount{
    	private BigDecimal balance;
    	private Currency currency = Currency.getInstance(Locale.US);
    	
    	public DollarAmount(BigDecimal balance){
    		this.balance = balance;
    	}
    	
    	public BigDecimal getBalance() {
    		return balance;
    	}
    
    	public void setAmount(BigDecimal amount) {
    		this.balance = amount;
    	}
    
    	public Currency getCurrency() {
    		return currency;
    	}
    
    	public void setBalance(BigDecimal balance) {
    		this.balance = balance;
    	}
    }
    

    Account 类,提供账户的借、贷方法。

    /**
     * 账户类:包括金额,借贷及获取余额方法
     * @author bh
     */
    public class Account {
    	private String id;
    	private Amount balance;
    	
    	public Account(Amount amount,String id){
    		this.balance = amount;
    		this.id = id;
    	}
    	
    	public Amount getBalance() {
    		return balance;
    	}
    
    	public void setBalance(Amount balance) {
    		this.balance = balance;
    	}
    
    	public void debit(Amount amount){
    		if(this.balance==null||amount==null||amount.getBalance()==null){
    			return;
    		}
    		
    		System.out.println(id+" 支出金额"+amount.getBalance());
    		
    		//修正账户余额:本账户减去借方金额
    		BigDecimal current = this.balance.getBalance();
    		BigDecimal now = current.subtract(amount.getBalance());
    		this.balance.setBalance(now);
    	}
    	
    	public void credit(Amount amount){
    		if(this.balance==null||amount==null||amount.getBalance()==null){
    			return;
    		}
    		
    		System.out.println(id+" 收入金额"+amount.getBalance());
    		
    		//修正账户余额:本账户加贷方金额
    		BigDecimal current = this.balance.getBalance();
    		BigDecimal now = current.add(amount.getBalance());
    		this.balance.setBalance(now);
    	}
    
    	public String getId() {
    		return id;
    	}
    
    	public void setId(String id) {
    		this.id = id;
    	}
    }
    

    AccountHelper 转账功能提供者,在执行转账之前,先获取两个账户的锁:

    /**
     * 同时获取两个账户的锁
     * @author bh
     */
    public class AccountHelper {
    	public void transferMoney(final Account fromAcct,
    			final Account toAcct,
    			final Amount amount){
    		//参数校验
    		if(fromAcct==null||toAcct==null||amount==null){
    			throw new IllegalArgumentException("null arg.");
    		}
    		
    		//余额校验
    		if(fromAcct.getBalance().compareTo(amount)<0){
    			throw new IllegalArgumentException(fromAcct.getId()+"账户余额不足");
    		}
    		
    		synchronized (fromAcct) {
    			synchronized (toAcct) {
    				this.transfer(fromAcct, toAcct, amount);
    			}
    		}
    	}
    	
    	//transfer对两个账户的操作必须是原子的完成
    	private void transfer(final Account fromAcct,
    			final Account toAcct,
    			final Amount amount){
    		System.out.println("Thread "+Thread.currentThread().getName()+" do transfer.");
    		fromAcct.debit(amount);
    		toAcct.credit(amount);
    	}
    }
    

    上述代码看似没有问题,却存在死锁的风险。代码中先对 from 加锁,再对 to 账户加锁,貌似顺序固定,但事实上锁的顺序是动态的,它取决于参数传递的顺序,而参数的顺序又由外部输入决定。

    编写测试类

    编写测试代码,同时启动四个线程交替执行 transferMoney 方法,参数顺序相反。
    代码如下:

    public class MainTest {
    	public static void main(String[] args) {
    		Amount amFromAcc = new DollarAmount(new BigDecimal(2000));
    		Amount amToAcc = new DollarAmount(new BigDecimal(1000));
    		final AccountHelper h = new AccountHelper();
    		
    		final Account fromAcc = new Account(amFromAcc,"zhang_3");
    		final Account toAcc = new Account(amToAcc,"wang_5");
    		final Amount amToTran = new DollarAmount(new BigDecimal(1));
    		Thread t1 = new Thread(new Runnable(){
    			@Override
    			public void run() {
    				h.transferMoney(fromAcc, toAcc, amToTran);
    			}
    			
    		});
    		Thread t4 = new Thread(new Runnable(){
    			@Override
    			public void run() {
    				h.transferMoney(fromAcc, toAcc, amToTran);
    			}
    		});
    		Thread t2 = new Thread(new Runnable(){
    			@Override
    			public void run() {
    				h.transferMoney(toAcc, fromAcc, amToTran);
    			}
    		});
    		Thread t3 = new Thread(new Runnable(){
    			@Override
    			public void run() {
    				h.transferMoney(toAcc, fromAcc, amToTran);
    			}
    		});
    		t1.start();
    		t2.start();
    		t3.start();
    		t4.start();
    	}
    }
    

    反复执行该方法,前三次恰巧能够正常运行,在第四次执行的时候,终于遭遇了死锁,只有两个线程顺利结束,另外两个线程陷入死锁状态,这个程序一直结束不了。
    结果如下:
    在这里插入图片描述

    破解之道

    因无法控制参数的顺序,所以必须定义一种固定的锁顺序,保证程序的锁顺序不受参数的影响。书中给出的解决办法是:比较对象的 hashCode,以此固定锁顺序,当两个对象拥有相同的哈希值时,再使用加时赛锁固定加锁的顺序。

    修正 AccountHelper 代码如下:

    /**
     * 根据某种规则设置锁的获取顺序,避免锁顺序死锁问题
     * @author bh
     *
     */
    public class AccountHelper {
    	//加时赛锁
    	private final Object tieLock = new Object();
    	
    	public void transferMoney(final Account fromAcct,
    			final Account toAcct,
    			final Amount amount){
    		//参数校验
    		if(fromAcct==null||toAcct==null||amount==null){
    			throw new IllegalArgumentException("null arg.");
    		}
    		
    		//余额校验
    		if(fromAcct.getBalance().compareTo(amount)<0){
    			throw new IllegalArgumentException(fromAcct.getId()+"账户余额不足");
    		}
    		
    		//根据对象的hash值随机设置加锁顺序
    		int fromHash = System.identityHashCode(fromAcct);
    		int toHash = System.identityHashCode(toAcct);
    		
    		if(fromHash<toHash){
    			synchronized (fromAcct) {
    				synchronized (toAcct) {
    					this.transfer(fromAcct, toAcct, amount);
    				}
    			}
    		}else if(fromHash>toHash){
    			synchronized (toAcct) {
    				synchronized (fromAcct) {
    					this.transfer(fromAcct, toAcct, amount);
    				}
    			}
    		}else{
    			//碰巧相对时,先获取加时赛锁
    			synchronized (tieLock) {
    				synchronized (fromAcct) {
    					synchronized (toAcct) {
    						this.transfer(fromAcct, toAcct, amount);
    					}
    				}
    			}
    		}
    	}
    	
    	//transfer对两个账户的操作必须是原子的完成
    	private void transfer(final Account fromAcct,
    			final Account toAcct,
    			final Amount amount){
    		System.out.println("Thread "+Thread.currentThread().getName()+" do transfer.");
    		fromAcct.debit(amount);
    		toAcct.credit(amount);
    	}
    }
    

    实践启示录

    上述例子清晰地解释了过度使用锁的风险,例子可能有些极端。笔者不禁在想,真正开发过程中,谁会写这样的代码呢?为什么要同时获取多个锁呢?

    此外,在解决动态锁顺序死锁问题中,额外使用加时赛锁,完全可以只用这一个锁来保证转账操作的原子性呀。不过,笔者不得不承认,这段代码扩宽了个人的眼界和编程思维:原来还存在这样的问题,还可以用这样的解决办法。

    验证代码遭遇死锁时,Eclipse 的控制台一直显示红色运行状态,笔者起初觉得挺好玩的,随即又意识到了死锁对应用程序的威胁。如果我们投产的应用遭遇这种情况怎么办呢?而且死锁是随机发生的,第二个例子要运行多次才能出现死锁,这也增加了问题排查的难度。

    这个章节看完,那个卡住的测试程序就是 “死锁” 最直观的解释。

    展开全文
  • 锁顺序死锁(抱死) /** * 锁顺序死锁 */ public class LeftRightDeadLock { private static Object left = new Object(); private static Object right = new Object(); public static void leftRight() { ...

    概念:当一个线程永远的持有一个锁,并且其他线程都尝试获得这个锁时,那么它们将永远被阻塞

    锁顺序死锁(抱死)

    /**
     * 锁顺序死锁
     */
    public class LeftRightDeadLock {
        private static Object left = new Object();
        private static Object right = new Object();
    
        public static void leftRight() {
            synchronized (left) {
                //添加延迟时间来模拟锁获取冲突
                synchronized (right) {
                    System.err.println("leftRight::");
                }
            }
        }
    
        public static void rightLeft()  {
            synchronized (right) {
                //添加延迟时间来模拟锁获取冲突
                synchronized (left) {
                    System.err.println("rightLeft::");
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            ExecutorService exec = Executors.newFixedThreadPool(2);
    
            for (int i = 0; i < 1000000; i++) {
                exec.execute(()->leftRight());
                exec.execute(()->rightLeft());
            }
    
            exec.shutdown();
            exec.awaitTermination(10000,TimeUnit.SECONDS);
        }
    
    }
    

    原理图

    两个线程试图以不同的顺序获得相同的锁,如果按照相同的顺序来请求锁,那么就不会出现循环依赖的加锁依赖性,因此也就不会产生死锁。

    wait
    永久等待
    尝试锁住right
    永久等待
    尝试锁住left
    B
    锁住right
    A
    锁住left

    如果线程都按照固定的顺序来获得锁,那么在程序中就不会出现锁顺序死锁的问题

    动态的锁顺序死锁

    还有一种情况是,取决于参数顺序而导致的锁顺序不同,也会出现锁顺序死锁

    /**
     * 动态锁顺序死锁
     */
    public class DynamicLeftRightDeadLock {
    
        /**
         * 转账问题
         * @param fromAccount 来源账户
         * @param toAccount 目标账户
         * @param amount 处理额度
         */
        public static void transferMoney(AtomicInteger fromAccount,
                                         AtomicInteger toAccount,
                                         Integer amount) {
            synchronized (fromAccount) {
                synchronized (toAccount) {
                    System.err.println("in::");
                    //该值小于参数
                    if (Integer.valueOf(fromAccount.get()).compareTo(amount)<0) {
                        throw new RuntimeException("");
                    }else {
                        fromAccount.addAndGet(-amount);
                        toAccount.addAndGet(amount);
                    }
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            ExecutorService exec = Executors.newFixedThreadPool(2);
            AtomicInteger a = new AtomicInteger(100);
            AtomicInteger b = new AtomicInteger(100);
    
            for (int i = 0; i < 1000000; i++) {
                exec.execute(()->transferMoney(a,b,1));
                exec.execute(()->transferMoney(b,a,1));
            }
    
            exec.shutdown();
            exec.awaitTermination(10000, TimeUnit.SECONDS);
        }
    
    }
    

    原理同上

    协作死锁

    协作对象持有锁的时候,调用外部的方法,那么将会出现活跃性问题。因为外部方法可能会获得其他锁(有可能就会产生死锁),如果阻塞时间过长,也会导致其他线程无法获取被当前线程持有的锁

    /**
     * 协作死锁
     */
    public class CooperationDeadLock {
    
        public static void main(String[] args) throws InterruptedException {
            ExecutorService exec = Executors.newFixedThreadPool(2);
            Dispatcher dispatcher = new Dispatcher();
            // 模拟五辆车反复进入调度
            Taxi[] taxis = new Taxi[5];
            for (int i = 0; i < 5; i++) {
                taxis[i] = new Taxi(dispatcher);
            }
    
            for (int i = 0; i < 100; i++) {
            //两个对象协作执行时,容易产生死锁
                exec.execute(()->{
                    int index = new Random().nextInt(5);
                    taxis[index].setDistance(0);
                });
                exec.execute(()->dispatcher.getSnapshot());
            }
            exec.shutdown();
            exec.awaitTermination(5, TimeUnit.SECONDS);
        }
    
    }
    
    /**
     * 出租车调度系统,调度获取出租车信息
     */
    class Taxi {
        private int distance;
        private final Dispatcher dispatcher;
    
        Taxi(Dispatcher dispatcher) {
            this.dispatcher = dispatcher;
        }
    
        public synchronized int getDistance() {
            return distance;
        }
    
        public synchronized void setDistance(int distance) {
            this.distance = distance;
            if (distance==0) {
                //外部锁
                dispatcher.notifyAvailable(this);
            }
        }
    }
    
    class Dispatcher {
        private Set<Taxi> availableTaxis;
    
        public Dispatcher() {
            availableTaxis = new HashSet<>();
        }
    
        public synchronized void notifyAvailable(Taxi taxi) {
            availableTaxis.add(taxi);
        }
    
        // 获取当前可调度车辆快照
        public synchronized String getSnapshot() {
            StringBuilder builder = new StringBuilder();
            for (Taxi taxi: availableTaxis) {
                //外部锁
                builder.append(taxi+": "+taxi.getDistance()+"\n");
            }
            return builder.toString();
        }
    }
    

    原理图
    同上,不同在于不是显式的使用双重锁,而是在持有锁的方法中调用了外部锁

    建议使用开放调用:如果在调用某个方法的时候不需要持有锁,那么就叫做开放调用;将锁的范围控制在方法内部,具体如下

        public void setDistance(int distance) {
            boolean isArrive;
            synchronized(this) {
                this.distance = distance;
                isArrive = distance==0;
            }
            
            if (isArrive) {
                //外部锁
                dispatcher.notifyAvailable(this);
            }
        }
    

    本内容示例主要来自《Java并发编程实战》

    展开全文
  • 通过锁顺序来避免动态的锁顺序死锁 前言   两个线程试图通过不同的顺序获取多个相同的锁。如果请求的顺序不相同,那么会出现循环的锁依赖现象,产生死锁。但是如果保证同时请求锁L和锁M的每一个线程,都是按照从...

    通过锁顺序来避免动态的锁顺序死锁

    欢迎关注作者博客
    简书传送门

    前言

      两个线程试图通过不同的顺序获取多个相同的锁。如果请求的顺序不相同,那么会出现循环的锁依赖现象,产生死锁。但是如果保证同时请求锁L和锁M的每一个线程,都是按照从 L 到 M 的顺序,那么就不会发生死锁了。
      比如:银行账户转账问题,两个用户转账的话,如果采用一般的synchronized嵌套的话,容易造成死锁。

    思想

      我们可以制定锁的顺序,并在整个应用程序中,获得锁都必须始终遵守这个既定的顺序。我们在制定对象顺序的时候,可以使用System.identityHashCode这样一种方式,它会返回Object.hashcode所返回的值。 在极少数的情况下,2个对象具有相同的哈希码,我们必须使用任意的中数来决定锁的顺序,这又重新引入了死锁的可能性。这个时候我们使用另一个锁(加时赛锁),在获得2个对象的锁之前,就要获得这个锁。

    /**
     * @program:
     * @description: 动态的锁顺序死锁的解决方案——通过锁顺序来避免死锁
     * @author: zhouzhixiang
     * @create: 2018-11-16 20:03
     */
    public class ThreadTest5 {
    
        // 加时赛锁
        private static final Object tieLock = new Object();
    
        class Account implements Comparable{
            String username;
            String password;
            long moneycount;
    
            void debit(long amount) {
                moneycount = moneycount - amount;
            }
    
            void credit(long amount) {
                moneycount = moneycount + amount;
            }
    
            @Override
            public int compareTo(Object o) {
                return (int) (moneycount - ((long)o));
            }
        }
    
        /**
         * 此方法容易发生动态的锁顺序死锁——错误方式
         * @param fromAccount 转账方
         * @param toAccount   收账方
         * @param amount      转账金额
         */
        public void tranferMoney(Account fromAccount, Account toAccount, long amount) throws InsufficientResourcesException {
            synchronized(fromAccount) {
                synchronized (toAccount) {
                    if(fromAccount.compareTo(amount) < 0)
                        throw new InsufficientResourcesException();
                    else {
                        fromAccount.debit(amount);
                        toAccount.credit(amount);
                    }
                }
            }
        }
    
        /**
         * 通过锁顺序来避免死锁——正确方法
         * @param fromAccount 转账方
         * @param toAccount   收账方
         * @param amount      转账金额
         */
        public void tranferMoney2(Account fromAccount, Account toAccount, long amount) throws InsufficientResourcesException {
            class Helper {
                public void tranfer() throws InsufficientResourcesException {
                    if(fromAccount.compareTo(amount) < 0)
                        throw new InsufficientResourcesException();
                    else {
                        fromAccount.debit(amount);
                        toAccount.credit(amount);
                    }
                }
            }
    
            int fromHashCode = System.identityHashCode(fromAccount);
            int toHashCode = System.identityHashCode(toAccount);
    
            if(fromHashCode < toHashCode) {
                synchronized (fromAccount) {
                    synchronized (toAccount) {
                        new Helper().tranfer();
                    }
                }
            }else if (fromHashCode > toHashCode) {
                synchronized (toAccount) {
                    synchronized (fromAccount) {
                        new Helper().tranfer();
                    }
                }
            }else {
                // 加时赛锁
                synchronized (tieLock) {
                    synchronized (fromAccount) {
                        synchronized (toAccount) {
                            new Helper().tranfer();
                        }
                    }
                }
            }
        }
    
    
    }
     
    

    欢迎加入Java猿社区
    欢迎加入Java猿社区.png

    展开全文
  • 锁顺序死锁 两个线程试图通过不同的顺序获取多个相同的锁。如果请求的顺序不相同,那么会出现循环的锁依赖现象,产生死锁。但是如果保证同时请求锁L和锁M的每一个线程,都是按照从 L 到 M 的顺序,那么就不会发生...

    精诚所至,金石为开。———《后汉书·广陵思王荆传》
    意思是人的诚心所到,能感动天地,使金石为之开裂。比喻只要专心诚意去做,什么疑难问题都能解决。

    锁顺序死锁

    两个线程试图通过不同的顺序获取多个相同的锁。如果请求的顺序不相同,那么会出现循环的锁依赖现象,产生死锁。但是如果保证同时请求锁L和锁M的每一个线程,都是按照从 L 到 M 的顺序,那么就不会发生死锁了。

    举个例子说明一下,让我们更加直观的了解顺序死锁问题,请看下面代码:

    public class ThreadDeadLockTest{
    
        private final Object left = new Object();
        private final Object right = new Object();
    
        public void leftRight() {
            synchronized (left) {
                synchronized (right) {
                    doSomething();
                }
            }
        }
    
        public void rightLeft() {
            synchronized (right) {
                synchronized (left) {
                    doSomething();
                }
            }
        }
    }

    如果一个线程调用了 leftRight, 另一个线程调用了 rightLeft,这样的交替运行,那么它们会发生死锁。线程 A 拿到了left锁,而线程 B 拿到了 right 锁,这个时候就陷入了相互等待的循环了。

    解决的办法是,如果所有线程以通用的固定秩序获得锁,程序就不会出现锁顺序死锁问题了。

    动态的锁顺序死锁

    然而,有些时候并不是那么容易避免顺序死锁的发生。比如,下面这个代码:

    public class ThreadDeadLockTest{
    
        public void transferMoney(Account fromAccount, Account toAccount, Amount money) {
    
            synchronized (fromAccount) {
                synchronized (toAccount) {
                    if(fromAccount < money) {
                        throw new InsufficientFundsException();
                    } else {
                        fromAccount.debit(money);
                        toAccount.credit(money);
                    }
                }
            }
        }
    }

    针对上面程序,如果两个线程同时调用transferMoney, 一个从 A 向 B 转账,另一个从 B 向 A 转账,那么就会发生死锁。如下代码调用所示:

        transferMoney(myAccount, yourAccount, 10);
        transferMoney(yourAccount, myAccount, 20);

    可以看出,锁的顺序就是参数的顺序,它超出了我们的控制。那么,有么有办法来制定一个锁的规范,保证锁的顺序是一致的呢?

    答案是肯定的,现在我们制定一种锁的顺序,并在整个应用程序都遵循它。我们可以使用

    System.identityHashCode

    的方式,它返回 Object.hashCode, 唯一一个值。但是,我们还得想如何规避哈希值冲突产生的问题。哈希冲突,我们可以再引入一个锁,当哈希冲突的时候,必须获取这个锁,从而保证一次只有一个线程执行哈希冲突的情况。

    请看下面代码,保证了锁顺序的一致,避免了死锁的发生。

    public class ThreadTest{
        //额外锁,针对哈希值冲突的情况
        private static final Object tieLock = new Object();
    
        public void transferMoney(Account fromAccount, Account toAccount, Amount money) {
    
            class Helper{
                public void transfer() {
                    if(fromAccount < money) {
                        throw new InsufficientFundsException();
                    } else {
                        fromAccount.debit(money);
                        toAccount.credit(money);
                    }
                }
            }
            //或许哈希值
            int fromHashCode = System.identityHashCode(fromAccount);
            int toHashCode = System.identityHashCode(toAccount);
    
            if(fromHashCode < toHashCode) {
                synchronized (fromAccount) {
                    synchronized (toAccount) {
                        new Helper().transfer();
                    }
                }
            } else if(fromHashCode > toHashCode) {
                synchronized(toAccount) {
                    synchronized(fromAccount) {
                        new Helper().transfer();
                    }
                }
            } else {
                synchronized(tieLock) {
                    synchronized(fromAccount) {
                        synchronized(toAccount) {
                            new Helper().transfer();
                        }
                    }
                }
            }
        }
    }

    上面程序就保证了锁顺序的一致性,从而规避了锁顺序死锁的情况。很多时候不易察觉这种动态的锁顺序,我们需要更加细心的使用多个锁,尽早的制定锁顺序规范。

    展开全文
  • 在极少数情况,恰好第一个线程取得1,还没来得及取得2,第二个线程取得2,然后还没来得及取得1,这样会死锁. 如果有三个. 顺序要不是1-2-3,要不是1-3-2,没有构成环形,这种应该就能避免了.具体解释等以后我...
  • 我们可以制定顺序,并在... 在极少数的情况下,2个对象具有相同的哈希码,我们必须使用任意的中数来决定顺序,这又重新引入了死锁的可能性。使用另一个,在获得2个对象的之前,就要获得这个 以下程...
  • * 通过制定确定的锁顺序来避免死锁 * @author xiaof * */ public class DeathLock { public void transferMoney(Account fromAccount, Account toAccount, int amount) throws ...
  • 简单的锁顺序死锁(不要这么做)

    千次阅读 2013-08-29 14:08:01
    public class LeftRightDeadLock {  private final Object left = new Object();    private final Object right = new Object();    public void leftRight()  { ... synchronized
  • 上述程序容易形成死锁,原因在于多账户调用TransMoney.transferMoney时,存在锁顺序冲突, 解决方案是使用System.identityHashCode来定义锁的顺序,消除死锁的可能性,代码实现如下: public static void ...
  • 死锁锁顺序

    2018-06-11 22:44:06
    1、由于不同线程对两个锁获取的顺序不一致造成的死锁,叫做锁顺序死锁。2、代码示例如下:/** * LeftRightDeadlock * * Simple lock-ordering deadlock * * @author Brian Goetz and Tim Peierls */ public ...
  • 死锁之动态锁顺序

    2018-06-11 23:12:28
    1、由于方法入参由外部传递而来,方法内部虽然对两个参数按照固定顺序进行加锁,但是由于外部传递时顺序的不可控,而产生锁顺序造成的死锁,即动态锁顺序死锁。2、示例代码如下:// Warning: deadlock-prone! public...
  • 典型的顺序死锁

    2016-04-10 20:21:30
    import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; ... * 死锁的出现分2中情况: * 1.... * 本例演示的是锁顺序死锁 * 当一个线程永远占用一个锁,而其他线
  • Java通过顺序避免死锁

    千次阅读 2015-05-26 19:51:12
    内容:通过获取顺序来避免死锁。例如:银行账户转账问题,两个用户转账的话,如果采用一般的synchronized嵌套的话,容易造成死锁,现在我们通过类似哲学家问题的解决方案一样:先获取同一个,才有资格获取下一...
  • 一般来说,死锁产生的原因是因为获取顺序不一致,所以如果有顺序的将写入程序内。就可以解决死锁,但是如果是某个方法的入参,那么就会造成动态死锁问题,比如说你设置了两把,分别为方法的两个入参a,b,...
  • java-并发--死锁

    2020-08-02 22:28:48
    什么是死锁: ...2. 如果必须要使用多个,尽量设计好的获取顺序,可以将对象和的关系图形化展示出来 3. 使用超时机制 4. 通过不同的工具进行代码分析,查找出可能出现死锁的场景。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,151
精华内容 460
关键字:

锁顺序死锁