浅谈Java由于不当的执行顺序导致的死锁
作者:flydean 发布时间:2022-08-05 22:05:33
标签:Java,死锁
我们来讨论一个经常存在的账户转账的问题。账户A要转账给账户B。为了保证在转账的过程中A和B不被其他的线程意外的操作,我们需要给A和B加锁,然后再进行转账操作, 我们看下转账的代码:
public void transferMoneyDeadLock(Account from,Account to, int amount) throws InsufficientAmountException {
synchronized (from){
synchronized (to){
transfer(from,to,amount);
}
}
}
private void transfer(Account from,Account to, int amount) throws InsufficientAmountException {
if(from.getBalance() < amount){
throw new InsufficientAmountException();
}else{
from.debit(amount);
to.credit(amount);
}
}
看起来上面的程序好像没有问题,因为我们给from和to都加了锁,程序应该可以很完美的按照我们的要求来执行。
那如果我们考虑下面的一个场景:
A:transferMoneyDeadLock(accountA, accountB, 20)
B:transferMoneyDeadLock(accountB, accountA, 10)
如果A和B同时执行,则可能会产生A获得了accountA的锁,而B获得了accountB的锁。从而后面的代码无法继续执行,从而导致了死锁。
对于这样的情况,我们有没有什么好办法来处理呢?
加入不管参数怎么传递,我们都先lock accountA再lock accountB是不是就不会出现死锁的问题了呢?
我们看下代码实现:
private void transfer(Account from,Account to, int amount) throws InsufficientAmountException {
if(from.getBalance() < amount){
throw new InsufficientAmountException();
}else{
from.debit(amount);
to.credit(amount);
}
}
public void transferMoney(Account from,Account to, int amount) throws InsufficientAmountException {
int fromHash= System.identityHashCode(from);
int toHash = System.identityHashCode(to);
if(fromHash < toHash){
synchronized (from){
synchronized (to){
transfer(from,to, amount);
}
}
}else if(fromHash < toHash){
synchronized (to){
synchronized (from){
transfer(from,to, amount);
}
}
}else{
synchronized (lock){
synchronized (from) {
synchronized (to) {
transfer(from, to, amount);
}
}
}
}
}
上面的例子中,我们使用了System.identityHashCode来获得两个账号的hash值,通过比较hash值的大小来选定lock的顺序。
如果两个账号的hash值恰好相等的情况下,我们引入了一个新的外部lock,从而保证同一时间只有一个线程能够运行内部的方法,从而保证了任务的执行而不产生死锁。
来源:https://www.cnblogs.com/flydean/p/12680253.html


猜你喜欢
- 概述现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。J
- 介绍上图就是循环依赖的三种情况,虽然方式不同,但是循环依赖的本质是一样的,就A的完整创建要依赖与B,B的完整创建要依赖于A,相互依赖导致没办
- List 是在开发中比较常用的集合,今天总结一下 Java 中初始化 List 的几种方式。1、常规方式List<String>
- 前言我们来分析一下堆内布局以及Java对象在内存中的布局吧。对象的指向先来看一段代码:package com.zwx.jvm;public
- 目录前言1、什么叫循环依赖呢2、具体出现循环依赖的代码逻辑3、解决循环依赖的代码实现总结前言本文基于springboot版本2.5.1 &n
- 翻译自 John Demetriou 2019年2月17日 的文章 《C# 8 – Introducing Index Struct And
- C语言 strcmp() 函数用于对两个字符串进行比较(区分大小写)。头文件:string.h语法/原型:int strcmp(const
- 在游戏项目中我们常常看到商城的广告牌,几张广告图片循环滚动,类似跑马灯,现在我将讨论一种实现方法,并提供一个管理类,大家可以直接使用。实现原
- 案例:public interface ForumService { void removeTopic(int topicId); void
- 问题是这样的,我用eclipse发送httpclient请求如下没有问题,但是在idea中就返回400,为毛呢???excuse me?pa
- 之前文章中我们讲到,java中实现同步的方式是使用synchronized block。在java 5中,Locks被引入了,来提供更加灵活
- 前言之前写过一篇关于配置中心对配置内容加密解密的介绍:《Spring Cloud构建微服务架构:分布式配置中心(加密解密) 》。在这篇文章中
- 编写一个 Java 应用程序,实现图形界面多人聊天室(多线程实现),要求聊天室窗口标题是 “欢迎使用 XXX 聊天室应用
- 前言基于安卓平台的连续滚动图像组件ContinuousScrollableImageView(https://github.com/Cutt
- 今天深度学习一下《Java并发编程的艺术》的第1章并发编程的挑战,深入理解Java多线程,看看多线程中的坑。注意,哈肯的程序员读书笔记并不是
- 23种设计模式第四篇:java单例模式定义: 单例模式,
- 一.模拟问题最近在公司遇到一个问题,挂号系统是做的集群,比如启动了两个相同的服务,病人挂号的时候可能会出现同号的情况,比如两个病人挂出来的号
- IDEA打成jar包并在windows后台运行一、IDEA打成jar包1、File=>Project Structure=>Pr
- 本文介绍了JAVA中实现原生的 socket 通信机制原理,分享给大家,具体如下:当前环境jdk == 1.8知识点socket 的连接处理
- Dagger2注入框架原理简要分析使用Dagger2需要的依赖:implementation 'com.google.dagger: