并发编程-19AQS同步组件之重入锁ReentrantLock、 读写锁ReentrantReadWriteLock、Condition

简介: 并发编程-19AQS同步组件之重入锁ReentrantLock、 读写锁ReentrantReadWriteLock、Condition

2019080611330380.jpg


J.U.C脑图


2019022410360263.png


20190224103319901.png

2019022410351861.png

ReentrantLock概述

重入锁ReentrantLock,顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对

资源的重复加锁,而不会造成自己阻塞自己。


重进入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁所阻塞


ReentrantLock虽然没能像synchronized关键字一样支持隐式的重进入,但是在调用lock()方

法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞。


除此之外,该锁的还支持获取锁时的公平和非公平性选择。实际上,公平的锁机制往往没有非公平的效率高,但是,并不是任何场景都是以TPS作为唯一的指标,公平锁能够减少“饥饿”发生的概率,等待越久的请求越是能够得到优先满足。看使用场景。


公平性锁保证了锁的获取按照FIFO原则,而代价是进行大量的线程切换。非公平性锁虽然可能造成线程“饥饿”,但极少的线程切换,保证了其更大的吞吐量。


在Java里一共有两类锁, 一类是synchornized同步锁,还有一种是JUC里提供的锁Lock,Lock是个接口,其核心实现类就是ReentrantLock。


ReentrantLock实现 ,主要是采用自旋锁,循环调用CAS操作来实现加锁,避免了使线程进入内核态的阻塞状态


ReentrantLock独有的功能


可指定是公平锁还是非公平锁,所谓公平锁就是先等待的线程先获得锁

提供了一个Condition类,可以分组唤醒需要唤醒的线程

提供能够中断等待锁的线程的机制,lock.lockInterruptibly()


ReentrantLock 常用方法

20190224225519336.png

void  lock()   //加锁 
void  unlock()  //释放锁
boolean isHeldByCurrentThread();   // 当前线程是否保持锁定
boolean isLocked()  // 是否存在任意线程持有锁资源
void lockInterruptbly()  // 如果当前线程未被中断,则获取锁定;如果已中断,则抛出异常(InterruptedException)
int getHoldCount()   // 查询当前线程保持此锁定的个数,即调用lock()方法的次数
int getQueueLength()   // 返回正等待获取此锁定的预估线程数
int getWaitQueueLength(Condition condition)  // 返回与此锁定相关的约定condition的线程预估数
boolean hasQueuedThread(Thread thread)  // 当前线程是否在等待获取锁资源
boolean hasQueuedThreads()  // 是否有线程在等待获取锁资源
boolean hasWaiters(Condition condition)  // 是否存在指定Condition的线程正在等待锁资源
boolean isFair()   // 是否使用的是公平锁


synchronized 和 ReentrantLock的比较


image.png

顺便说下自旋锁:是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。


那该如何选择呢?


如果需要实现ReenTrantLock的三个独有功能时,就选择使用ReenTrantLock, 通常情况下synchronized就能够满足了,而且使用起来简单,由JVM管理,不会产生死锁。


ReentrantLock示例


我们把使用synchronized来确保线程安全的例子,使用ReentrantLock来实现下

20190224225220721.png

多次运行: 线程安全


20190224225245993.png

读写锁ReentrantReadWriteLock


可重入锁ReentrantLock是排他锁,这些锁在同一时刻只允许一个线程进行访问


而读写锁ReentrantReadWriteLock在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。即ReentrantReadWriteLock允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。


在没有任何读写锁的时候才能取得写入的锁,可用于实现悲观读取


读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写,使得并发性相比一般的排他锁有了很大提升。

20190224230243937.png


例子

假设现在有一个类,里面有一个map集合,希望在对其读写的时候能够进行一些线程安全的保护,这时我们就可以使用到ReentrantReadWriteLock

20190224233643425.png

StampedLock


StampedLock是Java8引入的一种新的锁机制,是读写锁的一个改进版本,读写锁虽然分离了读和写的功能,使得读与读之间可以完全并发,但是读和写之间依然是冲突的,读锁会完全阻塞写锁,它使用的依然是悲观的锁策略。如果有大量的读线程,它也有可能引起写线程的饥饿。而StampedLock则提供了一种乐观的读策略,这种乐观策略的锁非常类似于无锁的操作,使得乐观锁完全不会阻塞写线程。


示例

20190224234027251.png

运行结果: 线程安全


20190224234053804.png


Condition


任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait()、 wait(long timeout)、notify()以及notifyAll()方法,这些方法与synchronized同步关键字配合,可以实现等待/通知模式。Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式,但是这两者在使用方式以及功能特性上还是有差别的。


Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到 Condition对象关联的锁。Condition对象是由Lock对象(调用Lock对象的newCondition()方法)创建出来的,换句话说,Condition是依赖Lock对象的。


获取一个Condition必须通过Lock的newCondition()方法。


示例


Condition是一个多线程间协调通信的工具类,使得某个或者某些线程一起等待某个条件(Condition),只有当该条件具备( signal 或者 signalAll方法被调用)时 ,这些等待线程才会被唤醒,从而重新争夺锁。


Condition可以非常灵活的操作线程的唤醒,下面是一个线程等待与唤醒的例子,其中用1、2、3、4序号标出了日志输出顺序


20190224234547363.png


输出:

20190224234632296.png


代码


https://github.com/yangshangwei/ConcurrencyMaster

相关文章
|
3月前
|
安全 Java
JUC锁: ReentrantReadWriteLock详解
`ReentrantReadWriteLock` 主要用于实现高性能的并发读取,而在写操作相对较少的场景中表现尤为突出。它保证了数据的一致性和线程安全,在合适的场合合理使用 `ReentrantReadWriteLock`,可以实现更加细粒度的控制,并显著提升应用性能。然而,需要注意它的复杂度较一般的互斥锁高,因此在选择使用时要仔细考虑其适用场景。
42 1
|
5月前
|
存储 设计模式 安全
(五)深入剖析并发之AQS独占锁&重入锁(ReetrantLock)及Condition实现原理
在我们前面的文章《[深入理解Java并发编程之无锁CAS机制》中我们曾提到的CAS机制如果说是整个Java并发编程基础的话,那么本章跟大家所讲述的AQS则是整个Java JUC的核心。不过在学习AQS之前需要对于CAS机制有一定的知识储备,因为CAS在ReetrantLock及AQS中的实现随处可见。
|
5月前
|
安全 Java
Java多线程中的锁机制:深入解析synchronized与ReentrantLock
Java多线程中的锁机制:深入解析synchronized与ReentrantLock
96 0
|
6月前
|
监控 安全 Java
Java中的锁(Lock、重入锁、读写锁、队列同步器、Condition)
Java中的锁(Lock、重入锁、读写锁、队列同步器、Condition)
37 0
|
7月前
|
安全 Java 测试技术
ReentrantReadWriteLock(可重入读写锁)源码解读与使用
ReentrantReadWriteLock(可重入读写锁)源码解读与使用
|
算法
ReentrantLock 是如何实现可重入性的?
ReentrantLock 是如何实现可重入性的?
66 0
|
算法 调度
JUC基础(三)—— Lock锁 及 AQS(1)
JUC基础(三)—— Lock锁 及 AQS
136 0
|
Java
JUC基础(三)—— Lock锁 及 AQS(2)
JUC基础(三)—— Lock锁 及 AQS
96 0
ReentrantLock是如何实现可重入性
ReentrantLock是如何实现可重入性
75 0
AQS(abstractQueuedSynchronizer)锁实现原理详解
AQS(abstractQueuedSynchronizer)抽象队列同步器。其本身是一个抽象类,提供lock锁的实现。聚合大量的锁机制实现的共用方法。
156 0