探究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编程语言允许线程访问共享变量, 为了确保共享变量能被准确和一致地更新, ...
随机推荐
- GUI编程及文件对话框的使用
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import ...
- 两台linux服务器各有两个不同的用户 其中一个服务器可以无密码登录服务器
服务器A:普通用户USERA 服务器B:普通用户USERB 把USERA的公钥写入服务器B用户USERB的authorized_keys中 USERA能普能用户登录然后服务器A,然后再登录服务器B: ...
- Jupyter/JupyterLab安装使用
一.介绍 Jupyther notebook(曾经的Ipython notebook),是一个可以把代码.图像.注释.公式和作图集于一处,实现可读性及可视化分析的工具,支持多种编程语言.官方使用手册. ...
- [转]spring MultipartFile 转 File
原文地址:https://www.jianshu.com/p/6cf99d39e170 File.createTempFile(String prefix, String suffix); 创建一个临 ...
- C语言 · 猜算式 · 乘法竖式
题目:猜算式 你一定还记得小学学习过的乘法计算过程,比如: 273 x 15 ------ 1365 273 ------ 4095 请你观察如下的乘法算式 *** x *** ------- ...
- 通过反射调用Unity编辑器提供的各种功能
Unity编辑器功能丰富易上手,其实编辑器提供的大多数菜单操作,在代码里面都是能够找到对应接口的,但是这些接口都没有对我们开放,怎么办? 很简单,直接使用反射调用即可. 首先使用Reflector或I ...
- ubuntu install wiznote
sudo add-apt-repository ppa:wiznote-team #添加官方源 sudo apt-get update #更新源 sudo apt-get install wiznot ...
- Android 8 设置蓝牙名称 流程
记录android 8设置蓝牙名称的流程. packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothDeviceRenam ...
- centos系统安装rar解压工具unar
centOS上不支持rar解压,需要额外安装软件,收费版是unrar,免费版是unar unar在centOS上安装需要源码编译,下面是安装方法: 1.安装依赖 yum install gnustep ...
- 一种隐蔽性较高的Java ConcurrentModificationException异常场景
前言 在使用Iterator遍历容器类的过程中,如果对容器的内容进行增加和删除,就会出现ConcurrentModificationException异常.该异常的分析和解决方案详见博文<Jav ...