ReentrantLock学习笔记
参考:https://www.jianshu.com/p/4358b1466ec9
前言:
先来想象一个场景:手把手的进行锁获取和释放,先获得锁A,然后再获取锁B,当获取锁B后释放锁A同时获取锁C,当锁C获取后,再释放锁B同时获取锁D,以此类推,这种场景下,synchronized关键字就不那么容易实现了,而使用Lock却显得容易许多。
源码:
- public class ReentrantLock implements Lock, java.io.Serializable {
- private final Sync sync;
- abstract static class Sync extends AbstractQueuedSynchronizer {
- /**
- * Performs {@link Lock#lock}. The main reason for subclassing
- * is to allow fast path for nonfair version.
- */
- abstract void lock();
- /**
- * Performs non-fair tryLock. tryAcquire is implemented in
- * subclasses, but both need nonfair try for trylock method.
- */
- final boolean nonfairTryAcquire(int acquires) {
- final Thread current = Thread.currentThread();
- int c = getState();
- if (c == 0) {
- if (compareAndSetState(0, acquires)) {
- setExclusiveOwnerThread(current);
- return true;
- }
- }
- else if (current == getExclusiveOwnerThread()) {
- int nextc = c + acquires;
- if (nextc < 0) // overflow
- throw new Error("Maximum lock count exceeded");
- setState(nextc);
- return true;
- }
- return false;
- }
- protected final boolean tryRelease(int releases) {
- int c = getState() - releases;
- if (Thread.currentThread() != getExclusiveOwnerThread())
- throw new IllegalMonitorStateException();
- boolean free = false;
- if (c == 0) {
- free = true;
- setExclusiveOwnerThread(null);
- }
- setState(c);
- return free;
- }
- }
- //默认非公平锁
- public ReentrantLock() {
- sync = new NonfairSync();
- }
- //fair为false时,采用公平锁策略
- public ReentrantLock(boolean fair) {
- sync = fair ? new FairSync() : new NonfairSync();
- }
- public void lock() {
- sync.lock();
- }
- public void unlock() { sync.release(1);}
- public Condition newCondition() {
- return sync.newCondition();
- }
- ...
- }
使用方式:
- Lock lock = new ReentrantLock();
- Condition condition = lock.newCondition();
- lock.lock();
- try {
- while(条件判断表达式) {
- condition.wait();
- }
- // 处理逻辑
- } finally {
- lock.unlock();//保证锁在最后能够释放 注意!!!!一定要释放
- }
非公平锁:
当线程获取锁失败的时候,同样可以自旋获取锁,超过自旋次数才会进入队列尾部
- 线程A和B同时执行CAS指令,假设线程A成功,线程B失败,则表明线程A成功获取锁,并把同步器中的exclusiveOwnerThread设置为线程A。
- 竞争失败的线程B,在nonfairTryAcquire方法中,会再次尝试获取锁,Doug lea会在多处尝试重新获取锁,应该是在这段时间如果线程A释放锁,线程B就可以直接获取锁而不用挂起。
- static final class NonfairSync extends Sync {
- /**
- * Performs lock. Try immediate barge, backing up to normal
- * acquire on failure.
- */
- final void lock() {
- if (compareAndSetState(0, 1))
- setExclusiveOwnerThread(Thread.currentThread());
- else
- acquire(1);
- }
- public final void acquire(int arg) {
- if (!tryAcquire(arg) &&
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
- selfInterrupt();
- }
- protected final boolean tryAcquire(int acquires) {
- return nonfairTryAcquire(acquires);
- }
- }
公平锁:
在公平锁中,每当线程执行lock方法时,如果同步器的队列中有线程在等待,则直接加入到队列尾部。
- static final class FairSync extends Sync {
- private static final long serialVersionUID = -3000897897090466540L;
- final void lock() {
- acquire(1);
- }
- /**
- * Fair version of tryAcquire. Don't grant access unless
- * recursive call or no waiters or is first.
- */
- protected final boolean tryAcquire(int acquires) {
- final Thread current = Thread.currentThread();
- int c = getState();
- if (c == 0) {
- if (!hasQueuedPredecessors() &&
- compareAndSetState(0, acquires)) {
- setExclusiveOwnerThread(current);
- return true;
- }
- }
- else if (current == getExclusiveOwnerThread()) {
- int nextc = c + acquires;
- if (nextc < 0)
- throw new Error("Maximum lock count exceeded");
- setState(nextc);
- return true;
- }
- return false;
- }
- }
重入锁:
可重入锁指的是在一个线程中可以多次获取同一把锁,比如:
一个线程在执行一个带锁的方法,该方法中又调用了另一个需要相同锁的方法,则该线程可以直接执行调用的方法,而无需重新获得锁;
- if (current == getExclusiveOwnerThread()) {
- int nextc = c + acquires;
- if (nextc < 0)
- throw new Error("Maximum lock count exceeded");
- setState(nextc);
- return true;
- }
条件变量:
- public class ConditionObject implements Condition, java.io.Serializable {
- /** First node of condition queue. */
- private transient Node firstWaiter;
- /** Last node of condition queue. */
- private transient Node lastWaiter;
- public final void signal() {}
- public final void signalAll() {}
- public final void awaitUninterruptibly() {}
- public final void await() throws InterruptedException {}
- }
- Synchronized中,所有的线程都在同一个object的条件队列上等待。而ReentrantLock中,每个condition都维护了一个条件队列。
- 每一个Lock可以有任意数据的Condition对象,Condition是与Lock绑定的,所以就有Lock的公平性特性:如果是公平锁,线程为按照FIFO的顺序从Condition.await中释放,如果是非公平锁,那么后续的锁竞争就不保证FIFO顺序了。
- Condition接口定义的方法,await对应于Object.wait,signal对应于Object.notify,signalAll对应于Object.notifyAll。特别说明的是Condition的接口改变名称就是为了避免与Object中的wait/notify/notifyAll的语义和使用上混淆。
await实现逻辑:
- 将线程A加入到条件等待队列中,如果最后一个节点是取消状态,则从对列中删除。
- 线程A释放锁,实质上是线程A修改AQS的状态state为0,并唤醒AQS等待队列中的线程B,线程B被唤醒后,尝试获取锁,接下去的过程就不重复说明了。
- 线程A释放锁并唤醒线程B之后,如果线程A不在AQS的同步队列中,线程A将通过LockSupport.park进行挂起操作。
- 随后,线程A等待被唤醒,当线程A被唤醒时,会通过acquireQueued方法竞争锁,如果失败,继续挂起。如果成功,线程A从await位置恢复。
signal实现逻辑:
- 接着上述场景,线程B执行了signal方法,取出条件队列中的第一个非CANCELLED节点线程,即线程A。另外,signalAll就是唤醒条件队列中所有非CANCELLED节点线程。遇到CANCELLED线程就需要将其从队列中删除。
- 通过CAS修改线程A的waitStatus为0,表示该节点已经不是等待条件状态,并将线程A插入到AQS的等待队列中。
- 唤醒线程A,线程A和别的线程进行锁的竞争。
ReentrantLock学习笔记的更多相关文章
- ReentrantLock 学习笔记
有篇写的很不错的博客:https://blog.csdn.net/aesop_wubo/article/details/7555956 基于JDK1.8 参考着看源码 ,弄清楚lock()和un ...
- ReentrantLock 相关学习笔记
ReentrantLock 相关学习笔记 标签(空格分隔): Java多线程 Java接口Lock有三个实现类:ReentrantLock.ReentrantReadWriteLock.ReadLoc ...
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...
- JUC.Condition学习笔记[附详细源码解析]
目录 Condition的概念 大体实现流程 I.初始化状态 II.await()操作 III.signal()操作 3个主要方法 Condition的数据结构 线程何时阻塞和释放 await()方法 ...
- JUC.Lock(锁机制)学习笔记[附详细源码解析]
锁机制学习笔记 目录: CAS的意义 锁的一些基本原理 ReentrantLock的相关代码结构 两个重要的状态 I.AQS的state(int类型,32位) II.Node的waitStatus 获 ...
- Java多线程技术学习笔记(二)
目录: 线程间的通信示例 等待唤醒机制 等待唤醒机制的优化 线程间通信经典问题:多生产者多消费者问题 多生产多消费问题的解决 JDK1.5之后的新加锁方式 多生产多消费问题的新解决办法 sleep和w ...
- Java多线程学习笔记--生产消费者模式
实际开发中,我们经常会接触到生产消费者模型,如:Android的Looper相应handler处理UI操作,Socket通信的响应过程.数据缓冲区在文件读写应用等.强大的模型框架,鉴于本人水平有限目前 ...
- 学习笔记 07 --- JUC集合
学习笔记 07 --- JUC集合 在讲JUC集合之前我们先总结一下Java的集合框架,主要包含Collection集合和Map类.Collection集合又能够划分为LIst和Set. 1. Lis ...
- Java并发编程学习笔记
Java编程思想,并发编程学习笔记. 一.基本的线程机制 1.定义任务:Runnable接口 线程可以驱动任务,因此需要一种描述任务的方式,这可以由Runnable接口来提供.要想定义任务,只需实现R ...
随机推荐
- World Wind Java开发之一(转)
http://blog.csdn.net/giser_whu/article/details/40477235 参照<World wind Java三维地理信息系统开发指南随书光盘>以及官 ...
- 1.redis 安装
1.https://redis.io/download. 2. $ wget http://download.redis.io/releases/redis-3.2.9.tar.gz $ .tar.g ...
- 四面体ply格式文件图和数据对应关系分析
通过一个简单的文件来理解ply格式的文件是有所帮助的,我在网上找了一个四面体的ply文件,我通过meshlab打开看到的效果如下所示,我录制成gif文件,希望可以从不同角度展示出来: 同时我截图少许, ...
- CUDA中记录执行时间-GPU端
事件eventcudaEvent_t start,stop;cudaEventCreate(&start);cudaEventCreate(&stop);cudaEventRecord ...
- oracle用户创建及权限设置(转)
权限: create session create table unlimited tablespace connect resource dba 例: #sqlplus /nolog SQL> ...
- Java代码工具箱_用Set给List/Vector去重
参考 方法一:需要2个容器,1个迭代去重,1个作为结果容器. 此方法其实想法比较简单也是正常思路: package com.yonyou.test; import java.util.List; im ...
- LigerUi中表(Grid)控件的相关属性笔记
http://blog.csdn.net/dxnn520/article/details/8216560 // ========================================= [每 ...
- 转+更新 Graphviz 教程,例子+ 高级应用 写代码,编程绘制架构图(分层拓扑图) 转自官网
1. Graphviz介绍 Graphviz是大名鼎鼎的贝尔实验室的几位牛人开发的一个画图工具. 它的理念和一般的“所见即所得”的画图工具不一样,是“所想即所得”. Graphviz提供了dot语言来 ...
- MySQL 5.6常用参数配置及说明
[client] user = root password = Yong_110 [mysql] prompt = [\\u@\\p][\\d]>\\_ no-auto-rehash [mysq ...
- advanced regression to predict housing prices
https://docs.google.com/presentation/d/e/2PACX-1vQGlXP6QZH0ATzXYwnrXinJcCn00fxCOoEczPAXU-n3hAPLUfMfi ...