探究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编程语言允许线程访问共享变量, 为了确保共享变量能被准确和一致地更新, ...
随机推荐
- ubuntu代理设置
很多时候,服务器都没有连接外部互联网的条件,需要利用代理服务器才能够访问外网资源进行软件包的升级: ubuntu修改apt-get的代理可以使用代理进行安装包的升级: ubuntu@ubuntu:~$ ...
- 简单理解PHP-FPM
php-fpm只是一个php-fastcgi的管理器,为php提供管理服务 1.为什么会出现php-fpm fpm的出现全部因为php-fastcgi出现,为了很好的管理php-fastcgi而 ...
- 基于Vue element-ui实现支持多级纵向动态表头的仿表格布局
[本文出自天外归云的博客园] 需求图示如下,多级纵向动态表头表格: 我的思路是用element-ui的layout实现,做出一个仿造表格,能够支持动态的.多级的.纵向的表头: <template ...
- 【iCore4 双核心板_uC/OS-II】例程五:信号量——共享资源
一.实验说明: 信号量是操作系统中的一类事件,是实现任务间通信的一个中间环节.当系统中的多个任务 在运行时,经常需要互相无冲突地访问同一个资源,或者需要互相支持的依赖,甚至有时还要互 相加以必要的限制 ...
- 利用SEH防范BP(int 3)断点
利用SEH技术实现反跟踪,这个方法比单纯用判断API函数第一个字节是否为断点更加有效,可以防止在API函数内部的多处地址设置断点 通过int 3指令故意产生一个异常,从而让系统转入自己的异常处理函数, ...
- yii2快速導出phpexcel
https://packagist.org/packages/moonlandsoft/yii2-phpexcel 安装方式:首先是已经安装过Composer,则通过 Composer 下载安装 Mo ...
- 基于Gradle的spring boot 项目构建
今天听只是分享,听到不用maven而使用Gradle构建,就尝试了下 Java三大构建工具:Ant.Maven和Gradle Gradle是一个基于Apache Ant和Apache Maven概念的 ...
- 最全36种python设计模式
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用.设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案.这些解决方案是众多软件开发人员经过 ...
- docker应用-5(使用overlay 网络进行容器间跨物理主机通信)
同一个主机上的Docker容器之间通信 docker 引擎会在主机上增加一个docker0网卡,该网卡具有双重身份: 1.从容器视角,网桥(交换机)身份docker0 对于运行在同一个主机上的各个容器 ...
- mysql之表格的关联关系
1.’基本模式有多对一,多对多,一对一.关联的两个基本组建为外键列和参照列 典型的多对一模式,很普遍,如部门表和员工表,即一个部门可以有多个员工. 对于多对多的模式,就需要建立中间表,将其转换为多对一 ...