同步锁使用的弊端:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。这种情况能避免就避免掉。
~~~
synchronzied(A锁){
synchronized(B锁){
}
}
~~~
这是一个关于死锁的问题,代码如下:
![](https://box.kancloud.cn/7fa1241ea07016c645b8cea8a0003e50_1274x346.png)
很明显,这段代码在多线程情况下,会产生死锁:
假设线程1 做的操作是账户A给账户B转账, 先锁住了A账户, 接下来试图申请B账户的锁,
与此同时线程2 在从 账户B给账户A 转账, 先锁住了B账户的锁, 接下来试图申请A账户的锁。
两个线程各自持有资源, 然后等待获取对方的资源, 都无法执行下去, 死锁就出现了。
怎么写代码才能避免这种死锁呢?下面的代码是一种解决办法:
![](https://box.kancloud.cn/f1f68bbdaabf07196f681aafe2c31f8e_1032x916.jpg)
这段代码看起来有点吃力,但是如果你学过操作系统对死锁的处理的话, 就变的很容易。
操作系统中的理论是这样的: 如果有多个线程对多个资源进行访问时, 需要对资源进行排序(排序的方法你自己确定), 然后所有的线程都按同样的次序来访问资源,这样就不会出现环路等待了。
例如有10个线程, 每个都要访问多个资源, 对资源排序以后, 大家都先锁住1号资源进行访问(注意同一时刻,只有一个线程能获得锁, 别的都得等待) 然后是2号, 3号。。。
由于大家访问的次序是一样的, 就不会出现死锁的的情况。
理解了这一点, 对于上面的代码就很容易理解了, 实际就是对Account(账户)进行资源的排序, 通过 Java 内置的方法,得到Hashcode。 然后按顺序访问。
如果fromAccount 比较小, 那就先锁住fromAccount, 然后锁住toAccount, 反过来也是类似。
假设线程1 做的操作是账户A给账户B转账, 先锁住了A账户(假设A的hashcode 比较小), 接下来试图申请B账户的锁,
与此同时线程2 在从 账户B给账户A 转账, 由于A的hashcode 较小, 这个线程也试图先申请A的锁, 当然它申请不到, 因为已经被线程1持有了, 线程2只能等待
等到线程1完成操作以后, 线程2才能继续, 死锁就消除了。
如果两个账号的hashCode 相等怎么办, 没办法,只好引用一个第三方的锁了解决了, 这就是上述代码的else 分支 synchronized(lock) .
- 基础
- 编译和安装
- scanner类(键盘录入)
- Random类(随机数)
- 数组
- 方法
- 类
- ArrayList集合
- char与int
- eclipse
- IDEA
- 变量与常量
- 常用API
- String,StringBuffer,StringBuilder
- 正则,Date,DateFormat,Calendar
- 包装类,System,Math,Arrays,BigInteger,BigDecimal
- 集合,迭代器,增强for,泛型
- List,set,判断集合唯一
- map,Entry,HashMap,Collections
- 异常
- IO
- File
- 递归
- 字节流
- 字符流
- IO流分类
- 转换流
- 缓冲流
- 流的操作规律
- properties
- 序列化流与反序列化流
- 打印流
- commons-IO
- IO流总结
- 多线程
- 线程池
- 线程安全
- 线程同步
- 死锁
- lock接口
- ThreadLoad
- 等待唤醒机制
- 线程状态
- jdbc
- DBUtils
- 连接池DBCP
- c3p0连接池
- 网络编程
- 多线程socket上传图片
- 反射
- xml
- 设计模式
- 装饰器模式
- web service
- tomcat
- Servlet
- response
- request
- session和cookie
- JSP
- EL
- JSTL
- 事务
- 监听器Listener
- 过滤器Filter
- json
- linux安装软件
- 反射详解
- 类加载器和注解
- 动态代理
- jedis
- Hibernate
- 简介
- 创建映射文件
- Hibernate核心配置文件
- 事务和增删改查
- HibernateUtils
- 持久化对象的三种状态
- 检索方式
- query
- Criteria
- SQLQuery
- 持久化类
- 主键生成策略
- 缓存
- 事务管理
- 关系映射
- 注解
- 优化
- struts2
- 搭建
- 配置详解
- Action
- 结果跳转方式
- 访问ServletAPI方式
- 如何获得参数
- OGNL表达式
- valueStack 值栈
- Interceptor拦截器
- spring
- 导包
- IOC和DI
- Bean获取与实例化
- Bean属性注入
- spring注解
- 注解分层
- junit整合
- aop
- 动态代理实现
- cglib代理实现
- aop名词
- spring的aop
- aop-xml详解
- aop-注解详解
- 代理方式选择
- jdbcTemplate
- spring事务管理
- 回滚注意
- 事务传播属性
- MyBatis
- MyBatis简介
- 入门程序
- 与jdbc hibernate不同
- 原始Dao开发
- Mapper动态代理方式
- SqlMapConfig.xml配置文件
- 输入参数pojo包装类
- resultMap
- 动态sql
- 一对一关联
- 一对多
- 整合spring
- 逆向工程
- maven
- maven简介
- 仓库
- maven目录结构
- maven常用命令
- 生命周期
- eclipse中maven插件
- 入门程序
- 整合struct
- 依赖范围
- 添加插件
- idea配置
- jar包冲突
- 分模块开发
- 构建可执行的jar包(包含依赖jar包)
- springMVC
- 处理流程
- java面试
- java版本升级
- java1-8版本变更
- java9新特性
- 锁
- java资料
- idea
- jdk版本切换
- log4j
- 入门实例
- 基本使用方法
- Web中使用Log4j
- spring中使用log4j
- java代码优化