Java线程安全Lock、ReentrantLock、ReentrantReadWriteLock
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/120750932
本文出自【赵彦军的博客】
Java线程安全StampedLock
Java线程安全Lock、ReentrantLock、ReentrantReadWriteLock
Java线程安全集合总结
Java原子操作Atomic
文章目录
- 前言
- Lock
- ReentrantLock
- 公平锁/非公平锁
- 超时机制
- 可重入锁
- 读写锁 ReentrantReadWriteLock
- 源码结构
- 总结
- 示例
前言
java5之后,并发包中新增了Lock接口(以及相关实现类)用来实现锁的功能,它提供了与synchronized关键字类似的同步功能。
既然有了synchronized这种内置的锁功能,为何要新增Lock接口?先来想象一个场景:手把手的进行锁获取和释放,先获得锁A,然后再获取锁B,当获取锁B后释放锁A同时获取锁C,当锁C获取后,再释放锁B同时获取锁D,以此类推,这种场景下,synchronized关键字就不那么容易实现了,而使用Lock却显得容易许多。
Lock
Lock 是一个接口
public interface Lock {void lock();void lockInterruptibly() throws InterruptedException;boolean tryLock();boolean tryLock(long time, TimeUnit unit) throws InterruptedException;void unlock();Condition newCondition();
}
ReentrantLock
public class ReentrantLock implements Lock, java.io.Serializable {}
使用如下:
public class LockTest {Lock lock = new ReentrantLock();public void run(){//获取锁 lock.lock();//doSomething//释放锁lock.unlock();}
}
使用起来,也非常简单,首先 lock.lock();
获取锁,然后去执行自己的逻辑,最后调用 lock.unlock();
释放锁。
但是这里有个问题,如果我们的逻辑发生异常,那就永远无法释放锁,所以我们优化一下,把释放锁的逻辑放在 finally
块中,如下
public class LockTest {Lock lock = new ReentrantLock();public void run(){//获取锁lock.lock();try {//doSomething}catch (Exception e){}finally {//释放锁lock.unlock();}}
}
公平锁/非公平锁
非公平锁:如果同时还有另一个线程进来尝试获取,那么有可能会让这个线程抢先获取;
公平锁:如果同时还有另一个线程进来尝试获取,当它发现自己不是在队首的话,就会排到队尾,由队首的线程获取到锁。
ReentrantLock
主要利用CAS+AQS队列来实现。它支持公平锁和非公平锁,两者的实现类似。
- CAS:Compare and Swap,比较并交换。CAS有3个操作数:内存值V、预期值A、要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。该操作是一个原子操作,被广泛的应用在Java的底层实现中。在Java中,CAS主要是由sun.misc.Unsafe这个类通过JNI调用CPU底层指令实现
ReentrantLock主要利用CAS+AQS队列来实现。它支持公平锁和非公平锁,两者的实现类似。
AbstractQueuedSynchronizer 简称AQS
我们来看一下 ReentrantLock
构造函数
public ReentrantLock() {sync = new NonfairSync();}/*** Creates an instance of {@code ReentrantLock} with the* given fairness policy.** @param fair {@code true} if this lock should use a fair ordering policy*/public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}
构造支持传入 fair
来指定是否是公平锁
- FairSync() 公平锁
- NonfairSync() 非公平锁
如果使用无参构造函数,则是非公平锁。
超时机制
在 ReetrantLock的tryLock(long timeout, TimeUnit unit)
提供了超时获取锁的功能。它的语义是在指定的时间内如果获取到锁就返回true
,获取不到则返回false
。这种机制避免了线程无限期的等待锁释放。
可重入锁
可重入锁。可重入锁是指同一个线程可以多次获取同一把锁。ReentrantLock和synchronized都是可重入锁。
可中断锁。可中断锁是指线程尝试获取锁的过程中,是否可以响应中断。synchronized是不可中断锁,而ReentrantLock则提供了中断功能。
公平锁与非公平锁。公平锁是指多个线程同时尝试获取同一把锁时,获取锁的顺序按照线程达到的顺序,而非公平锁则允许线程“插队”。synchronized是非公平锁,而ReentrantLock的默认实现是非公平锁,但是也可以设置为公平锁。
读写锁 ReentrantReadWriteLock
现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。
在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写的操作了。
针对这种场景,JAVA的并发包提供了读写锁 ReentrantReadWriteLock
,它表示两个锁,一个是读操作相关的锁,称为共享锁;一个是写相关的锁,称为排他锁,描述如下:
线程进入读锁的前提条件:
没有其他线程的写锁,
没有写请求或者有写请求,但调用线程和持有锁的线程是同一个。
线程进入写锁的前提条件:
没有其他线程的读锁
没有其他线程的写锁
而读写锁有以下三个重要的特性:
(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。
(2)重进入:读锁和写锁都支持线程重进入。
(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁。
源码结构
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {/** 读锁 */private final ReentrantReadWriteLock.ReadLock readerLock;/** 写锁 */private final ReentrantReadWriteLock.WriteLock writerLock;final Sync sync;/** 使用默认(非公平)的排序属性创建一个新的 ReentrantReadWriteLock */public ReentrantReadWriteLock() {this(false);}/** 使用给定的公平策略创建一个新的 ReentrantReadWriteLock */public ReentrantReadWriteLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();readerLock = new ReadLock(this);writerLock = new WriteLock(this);}/** 返回用于写入操作的锁 */public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }/** 返回用于读取操作的锁 */public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }abstract static class Sync extends AbstractQueuedSynchronizer {}static final class NonfairSync extends Sync {}static final class FairSync extends Sync {}public static class ReadLock implements Lock, java.io.Serializable {}public static class WriteLock implements Lock, java.io.Serializable {}
}
1、类的继承关系
public class ReentrantReadWriteLockimplements ReadWriteLock, java.io.Serializable {}
说明:可以看到,ReentrantReadWriteLock实现了ReadWriteLock接口,ReadWriteLock接口定义了获取读锁和写锁的规范,具体需要实现类去实现;同时其还实现了Serializable接口,表示可以进行序列化,在源代码中可以看到ReentrantReadWriteLock实现了自己的序列化逻辑。
2、ReentrantReadWriteLock有五个内部类,五个内部类之间也是相互关联的。内部类的关系如下图所示。
说明:如上图所示,Sync继承自AQS、NonfairSync继承自Sync类、FairSync继承自Sync类(通过构造函数传入的布尔值决定要构造哪一种Sync实例);ReadLock实现了Lock接口、WriteLock也实现了Lock接口。
总结
在线程持有读锁的情况下,该线程不能取得写锁(因为获取写锁的时候,如果发现当前的读锁被占用,就马上获取失败,不管读锁是不是被当前线程持有)。
在线程持有写锁的情况下,该线程可以继续获取读锁(获取读锁时如果发现写锁被占用,只有写锁没有被当前线程占用的情况才会获取失败)。
仔细想想,这个设计是合理的:因为当线程获取读锁的时候,可能有其他线程同时也在持有读锁,因此不能把获取读锁的线程“升级”为写锁;而对于获得写锁的线程,它一定独占了读写锁,因此可以继续让它获取读锁,当它同时获取了写锁和读锁后,还可以先释放写锁继续持有读锁,这样一个写锁就“降级”为了读锁。
综上:
一个线程要想同时持有写锁和读锁,必须先获取写锁再获取读锁;写锁可以“降级”为读锁;读锁不能“升级”为写锁。
示例
public class LockTest {ReadWriteLock lock = new ReentrantReadWriteLock();Lock readLock = lock.readLock();Lock writeLock = lock.writeLock();}
Java线程安全Lock、ReentrantLock、ReentrantReadWriteLock相关推荐
- java 线程锁Lock
文章目录 说明 分享 资料 synchronized与Lock对比 Lock Lock 接口方法 实现类 ReentrantLock ReadWriteLock ReadWriteLock 接口方法 ...
- java线程系列---读写锁ReentrantReadWriteLock
获取顺序 此类不会将读取者优先或写入者优先强加给锁定访问的排序.但是,它确实支持可选的公平 策略.当公平地构造线程时,线程利用一个近似到达顺序的策略来争夺进入.当释放写入锁定后,将写入锁定分配给等待时 ...
- Java线程安全StampedLock
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/120854573 本文出自[赵彦军的博客] Java线程安全StampedLock ...
- Java线程安全集合总结
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/120749064 本文出自[赵彦军的博客] Java线程安全StampedLock ...
- 【java线程】锁机制:synchronized、Lock、Condition
[Java线程]锁机制:synchronized.Lock.Condition 原创 2013年08月14日 17:15:55 标签:Java /多线程 74967 http://www.infoq. ...
- java同步关键词解释、synchronized、线程锁(Lock)
1.java同步关键词解释 21.1 synchronized synchronized是用来实现线程同步的!!! 加同步格式: synchronized( 需要一个任意的对象(锁) ){ 代码块中放 ...
- 深圳Java培训:Lock线程锁
深圳Java培训:Lock线程锁 1:synchronized的缺陷 synchronized是java中的一个关键字,也就是说是Java语言内置的特性.那么为什么会出现Lock呢? 在上面一篇文章中 ...
- Java多线程(五) Lock接口,ReentranctLock,ReentrantReadWriteLock
在JDK5里面,提供了一个Lock接口.该接口通过底层框架的形式为设计更面向对象.可更加细粒度控制线程代码.更灵活控制线程通信提供了基础.实现Lock接口且使用得比较多的是可重入锁(Reentrant ...
- java lock 效率_工作常用4种Java线程锁的特点,性能比较、使用场景
多线程的缘由 在出现了进程之后,操作系统的性能得到了大大的提升.虽然进程的出现解决了操作系统的并发问题,但是人们仍然不满足,人们逐渐对实时性有了要求. 使用多线程的理由之一是和进程相比,它是一种非常花 ...
最新文章
- python使用matplotlib可视化线图(line plot)、设置X轴坐标的下限和上限数值(setting the lower and upper bound of the x axis)
- shell中的命令替换和变量替换
- 5. 连续时间马氏过程-强Markov族
- html 静态资源缓存时间,.htaccess设置静态资源缓存(即浏览器缓存)
- BZOJ.4555.[HEOI2016TJOI2016]求和(NTT 斯特林数)
- SAP 电商云 Spartacus UI 同 SAP Customer Data Cloud 的集成
- 网络原理题+复习资料
- 简洁版即时聊天---I/O多路复用使用
- Bootstrap3 滚动监听插件的调用方式
- 终于有人把云计算、大数据和人工智能讲明白了! (2)
- Android StrictMode类使用实例
- 软件测试 接口测试 入门Jmeter 接口关联 提取器 断言 与fiddler配合使用 使Jmeter录制和创建脚本 操作数据库 持续集成测试
- python可视化图表生成(一)
- 如何在vs2020编译c语言程序,如何在使用WIN10 Visual Studio 2015 编译FlightGear源码(2020.1.1版本)(示例代码)...
- 学 Python 知识点其实特简单, “内置函数“ 思维导图来了
- 知识竞赛中如何按抢答器才能最先抢到
- matlab 浮动波动率,Matlab计算股票价格波动率
- 网络:IP基础知识总结
- 第五章 数组及排序 ① 笔记
- html中显示框框中对勾,如何打出方框里有对勾
热门文章
- jmeter展示内存cpu_基于Docker的jmeter弹性压测(2)监控
- 三十六、 SQL基本语法
- K折交叉验证和pipeline
- 四十四、Python中的statsmodels模块
- 额外篇 | ggplot (下)
- opencv 裁剪 java_OpenCV绘制轮廓和裁剪
- 博士申请 | ​英属哥伦比亚大学李霄霄助理教授招收全奖博士生、硕士生
- 爱思唯尔云论坛:疫情挑战下的大数据与人工智能研究
- Greenplum【集群搭建 01】局域网 CentOS 7.9.2009 环境 GreenPlum 6.13.0 集群规划+配置+安装+内核参数调整(应用实例分享)
- SpringBoot 项目war包部署 配置外置tomcat方法