探究Java中的锁
一、锁的作用和比较
1、Lock接口及其类图

Lock接口:是Java提供的用来控制多个线程访问共享资源的方式。
ReentrantLock:Lock的实现类,提供了可重入的加锁语义
ReadWriteLock:读写锁的接口
ReentrantReadWriteLock: ReadWriteLock的实现类,维护一对锁,一个读锁(ReentrantReadWriteLock.ReadLock)和一个写锁(ReentrantReadWriteLock.writeLock),实现了锁的分离,提高了性能和吞吐量。也提供了可重入的加锁语义
ReentrantReadWriteLock.ReadLock:
ReentrantReadWriteLock.WriteLock:
Condition:条件队列接口,提供类似于Object监视器方法 ,可与Lock实现等待/通知模式
AbstractQueuedSynchronizer.ConditionObject: Condition的实现类
AbstractQueuedLongSynchronizer.ConditionObject:Condition的实现类
AbstractOwnableSynchronizer:可以被线程专有的同步器
AbstractQueuedSynchronizer(AQS):继承于AbstractOwnableSynchronizer,Java中构建锁和其他同步器的基础构建
AbstractQueuedLongSynchronizer:AbstractQueuedSynchronizer的一个版本,实现对Long型同步状态的同步。
(1)Lock与Synchronized同步锁、内置锁的的区别
加锁机制 |
特性 |
优点 |
缺点 |
适用范围 |
| Synchronize(内置锁) | 1、实现操作的互斥性和原子性 2、实现内存的可见性 3、禁止重排序 4、锁的获取和释放都是隐式的 5、每一个对象都是一个内置锁 6、必须在获取锁的代码块内释放,简化编码工作 7、锁的获取与释放都是基于代码块的(获取锁的操作和释放锁的操作在同一个代码块) |
1、JVM的内置属性(当 JVM 用 synchronized 管理锁定请求和释放时,JVM 在生成线程转储时能够包括锁定信息。这些对调试非常有价值,因为它们能标识死锁或者其他异常行为的来源。 2、可重入 3、无需实现锁的获取和释放 |
1、尝试获取已被独占的锁时线程会被阻塞 4、无法中断一个正在等候获得锁的线程,也无法通过轮询得到锁,如果不想等下去,也就没法得到锁。 5、同步还要求锁的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行,多数情况下,这没问题(而且与异常处理交互得很好),但是,确实存在一些非块结构的锁定更合适的情况 |
优先使用 2、监视器方法( wait(),waite(timeout),notify(),notifyAll() )与Synchronize实现通知/等待模式 |
ReentrantLock |
1、实现操作的原子性和互斥性 2、实现内存的可见性 3、禁止重排序 5、锁的获取和释放都是显示的 6、提供了可轮询、可定时、可中断的、公平性的锁获取机制 7、实现非块的加锁模式 |
1、性能优于内置锁 2、可重入 3、可在截止时间前获取锁 4、可非阻塞获取锁(尝试获取已被占用的锁的线程状态变更为等待,而不是阻塞) 5、可实现公平性锁 |
1、锁的获取和释放必须显示的执行锁的释放操作,不能制动清除锁 2、独占排他式锁,互斥锁,每次只能有一个线程持有该锁 |
1、需要使用锁的高级功能(可定时,可轮询,可以中断)的时候才使用LOCK 2、与Condition实现等待/通知模式 |
| ReentrantReadWriteLock | 1、分离了读锁和写锁,提供了读锁多线程共享访问和写锁独占访问 | 1、多读少写的情况下性能优于排他锁 | 在不是多读少写的情况下性能低于排它锁 | 多读少写 |
二、Java构建锁的基础组件AQS(探究锁的实现原理)
简介:队列同步器AbstractQueueSynchronizer(AQS)是用来构建锁或者其他同步组件的基础框架,使用一个int成员变量表示同步状态,通过内置FIFO队列来实现资源获取线程的的排队工作。
(AbstractQueuedLongSynchronizer是用Long型来表示同步状态)
使用模式:
通过子类继承AQS并实现它的抽象方法,并作为自定义同步组件的静态内部类,代理实现相关方法。
内置FIFO队列:AbstractQueueSynchronizer.Node表示队列节点,用来保存获取同步状态失败的线程引用、等待状态、前驱后继节点。
获取同步状态失败的节点添加到队列的尾部(通过CAS设置尾节点)
1、AQS独占式同步状态获取和释放的工作原理
独占式获取同步状态的获取失败的时生成的节点类型为Node.EXCLUSIVE(独占式),在加入到同步队列末尾后,进入节点自旋中,只至前驱节点为头结点且成功获取同步状态才可退出。
acqiure()获取锁,release()释放锁

2、AQS共享式同步状态获取和释放的工作原理
共享式获取同步状态时生成节点的类型为Node.SHARED(共享式),但是将节点插入到同步队列末尾的时候并不适用CAS。
tryAcquireShare() 获取同步状态
tryReleaseShared()通过循环和CAS实现线程安全的释放共享式锁。
3、独占式超时获取同步状态的工作原理
doAcquire(arg,nanosTimeout):超时获取同步状态
acquireInterruptibly(arg):可响应中断的获取同步状态
三、重入锁、公平锁、非公平锁
含义 |
优点 |
缺点 |
|
公平锁 |
锁的获取顺序严格按照锁等待的时间,等待时间越长的最优先获取 | 减少发生“线程饥饿”的概率 | 性能开销大(因为线程上下文切换次数多) |
| 非公平锁 | 锁的获取采用抢占式获取,锁的默认实现 | 性能开销小(因为进行线程上下文切换的次数少),提供更大的吞吐量 | 会出现“线程饥饿”问题 |
四、Condition接口
对比项 |
Object监视器方法 |
Condition |
| 前置条件 | 获取对象的锁 | Lock.lock() Condition condttion=Lock.newCondition() |
| 调用方式 | object.wait() | condition.wait() |
| 等待队列个数 | 一个 | 多个 |
| 同步队列 | 一个 | 一个 |
| 当前线程释放锁并进入等待状态 | 支持 | 支持 |
| 等待状态不响应中断 | 支持 | 支持 |
| 超时等待状态 | 支持 | 支持 |
| 定时等待状态 | 不支持 | 支持 |
| 唤醒等待队列中的一个线程 | 支持 | 支持 |
| 唤醒等待队列中的全部线程 | 支持 | 支持 |
| 响应中断 | 不支持 | 支持 |
| 实现等待/通知模式 | 与Synchronize搭配使用 | 与Lock搭配使用 |
探究Java中的锁的更多相关文章
- 初步探究java中程序退出、GC垃圾回收时,socket tcp连接的行为
初步探究java中程序退出.GC垃圾回收时,socket tcp连接的行为 今天在项目开发中需要用到socket tcp连接相关(作为tcp客户端),在思考中发觉需要理清socket主动.被动关闭时发 ...
- 深入介绍Java中的锁[原理、锁优化、CAS、AQS]
1.为什么要用锁? 锁-是为了解决并发操作引起的脏读.数据不一致的问题. 2.锁实现的基本原理 2.1.volatile Java编程语言允许线程访问共享变量, 为了确保共享变量能被准确和一致地更新, ...
- java 中的锁 -- 偏向锁、轻量级锁、自旋锁、重量级锁(转载)
之前做过一个测试,详情见这篇文章<多线程 +1操作的几种实现方式,及效率对比>,当时对这个测试结果很疑惑,反复执行过多次,发现结果是一样的: 1. 单线程下synchronized效率最高 ...
- Java 中的锁
Java中的锁分类 在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 公平锁/非公平锁 可重入锁 独享锁/共享锁 互斥锁/读写锁 乐观锁/悲观锁 分 ...
- Java中的锁(转)
Java中的锁 锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂.因为锁(以及其它更高级的线程同步机制)是由synchronized同步 ...
- java 中的锁 -- 偏向锁、轻量级锁、自旋锁、重量级锁
之前做过一个测试,详情见这篇文章<多线程 +1操作的几种实现方式,及效率对比>,当时对这个测试结果很疑惑,反复执行过多次,发现结果是一样的: 1. 单线程下synchronized效率最高 ...
- 深入理解Java中的锁
转载:https://www.jianshu.com/p/2eb5ad8da4dc Java中的锁 常见的锁有synchronized.volatile.偏向锁.轻量级锁.重量级锁 1.synchro ...
- JAVA 中无锁的线程安全整数 AtomicInteger介绍和使用
Java 中无锁的线程安全整数 AtomicInteger,一个提供原子操作的Integer的类.在Java语言中,++i和i++操作并不是线程安全的,在使用的时候, 不可避免的会用到synchron ...
- Java中的锁[原理、锁优化、CAS、AQS]
1.为什么要用锁? 锁-是为了解决并发操作引起的脏读.数据不一致的问题. 2.锁实现的基本原理 2.1.volatile Java编程语言允许线程访问共享变量, 为了确保共享变量能被准确和一致地更新, ...
随机推荐
- simHash 简介以及 java 实现
传统的 hash 算法只负责将原始内容尽量均匀随机地映射为一个签名值,原理上相当于伪随机数产生算法.产生的两个签名,如果相等,说明原始内容在一定概 率 下是相等的:如果不相等,除了说明原始内容不相等外 ...
- 我的IntelliJ IDEA 设置
1.关闭代码折叠 2.设置代码格式 3.函数参数提醒
- 关于VMware Linux 虚拟机忘记root 密码找回
因为昨天新安装过虚拟机设置了新的密码,再加上我好长时间没有用自己旧的虚拟机,导致忘记了密码,原来虽然知道在单用模式下,找回密码,但是确实是自己从来都没有做过,还好我们组大手飞翔哥告诉了我,怎么找回ro ...
- Machine Learning第十一周笔记:photo OCR
博客已经迁移至Marcovaldo's blog (http://marcovaldong.github.io/) 刚刚完毕了Cousera上Machine Learning的最后一周课程.这周介绍了 ...
- WPF Button 样式
WPF CheckBox 自定义样式 给Button设置ToolTip <Style TargetType="{x:Type Button}" x:Key="Def ...
- 大批量delete 优化方案
超过100万以上数据 删除的时候 会非常慢且产生大量日志文件 最大的问题是内存爆表 导致得多次重启服务才能删除整个库 暂时提出初步优化方案 1.设置日志为简单模式,处理完后恢复 ALTER DATAB ...
- 【规范】前端编码规范——css 规范
编码 在 css 首行设置文件编码为 UTF-8. @charset "UTF-8"; class 命名 class 名称应当尽可能短,并且意义明确.使用有意义的名称,使用有组织的 ...
- 手机配置代理报错invalid host header
手机配置代理后浏手机弹出页面报错invalid host header,因为我是用fiddler配置的,所以这时候就要看下自己配置完之后,是否重启,重启之后就没问题了. fiddle配置参考:http ...
- 字符串匹配的 KMP算法
一般字符串匹配过程 KMP算法是字符串匹配算法的一种改进版,一般的字符串匹配算法是:从主串(目标字符串)和模式串(待匹配字符串)的第一个字符开始比较,如果相等则继续匹配下一个字符, 如果不相等则从主串 ...
- 如何使用LinkedHashMap来实现一个LruCache
最近在看mybatis的源代码,发现了mybatis中实现的LruCache使用到了LinkedHashMap,所以就探究了一下LinkedHashMap是如何支持Lru缓存的 LinkedHashM ...

