ReentrantLock源码了解
1)、ReentrantLock.tryLock
//获取没有被其他线程持有的锁
//1)、当没有被任何线程持有时,首先将计数器设置为1,并设置当前持有锁的线程为当前线程,最后返回true
//2)、当被当前线程持有时,将计数器加1,最后返回true;
//3)、否则返回false
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
} //接着直接调用抽象类中的Sync.nonfairTryAcquire
final boolean nonfairTryAcquire(int acquires) {
//获取当前调用线程
final Thread current = Thread.currentThread();
//获取当前锁的状态,也就是当前锁的计数器的数值
int c = getState();
if (c == 0) {
//此时说明,当前锁没有被任何线程持有
if (compareAndSetState(0, acquires)) {
//使用CAS操作,更新锁的状态,即锁的计数器的数值,若是成功,则将当前锁的线程持有者设置为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
//根据JMM模型可知,同一个工作内存内是可见的,故同一个线程内是可见的,若相等,是同一个线程,且在同一个线程内不会发生setExclusiveOwnerThread时,在getExclusiveOwnerThread得到的不一样
//此时说明,当前锁的线程持有者是当前线程
//设置锁的计数器的值
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//更新锁的状态,即锁的计数值的数值
setState(nextc);
return true;
}
return false;
} 2)、ReentrantLock.unlock
//试图释放锁
//1)、当前线程不是该锁的持有者时,抛出异常
//2)、当前锁的计数器更改为0时,则设置当前锁的持有者为null
//3)、否则,更新当前锁的状态,即锁的计数器的数值
public void unlock() {
sync.release(1);
} //接着直接调用抽象类AbstractQueuedSynchronizer.release
public final boolean release(int arg) {
if (tryRelease(arg)) {
//当锁的状态为0,或者说当前锁没有持有者时,需要唤醒当前锁上挂起的线程
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
} //再接着直接调用抽象类Sync.tryRelease
protected final boolean tryRelease(int releases) {
//计算当前锁的计数器的数值
int c = getState() - releases;
//判断当前线程是否是锁的持有者,若不是,则抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
//当计数器的数值为0,说明此时锁没有持有者了,故先更新线程持有者,之后再去更新锁的状态,这样当去获取锁的状态时,此时的锁的持有者必然是更新后的,这样锁的释放和锁的获取就能保证一致的可见性了。
free = true;
setExclusiveOwnerThread(null);
}
//更新锁的状态,即锁的计数器的数值
setState(c);
return free;
} //直接调用AbstractQueuedSynchronizer.unparkSuccessor
//唤醒当前节点后的第一个非取消节点中的线程
private void unparkSuccessor(Node node) { //尝试更新当前节点的状态
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); //获取当前节点后的第一个非取消节点,并唤醒该节点中挂起的线程
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
//从尾节点开始查找,直到当前节点,即可得到一个当前节点后的第一个非取消节点
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
} //唤醒线程
if (s != null)
LockSupport.unpark(s.thread);
} 3)、ReentrantLock.lock
//申请锁
public void lock() {
sync.lock();
} 31)、非公平锁
//直接调用NonfairSync.lock
final void lock() {
//当通过CAS判断当前锁是否没有持有者,若是,则直接设置当前锁的持有者为当前线程;
//否则,再次尝试加锁,最后还不成功,就如等待队列,线程挂起,直到被唤醒为止
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
} //直接调用AbstractQueuedSynchronizer.acquire
public final void acquire(int arg) {
//尝试获取非公平锁,当失败时,就将当前线程入等待队列,线程挂起,直到被挂起
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
} //直接调用NonfairSync.tryAcquire
protected final boolean tryAcquire(int acquires) {
//尝试获取非公平锁
return nonfairTryAcquire(acquires);
} //直接调用AbstractQueuedSynchronizer.addWaiter
//将排他锁节点添加到链表尾部
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
//当前链表已经初始化,则CAS尝试将排他锁节点更改为链表的尾节点 //设置当前节点的前一个节点为当前链表的尾节点
node.prev = pred;
//当CAS尝试更新尾节点成功,则将新尾节点的前一个节点的下一个节点更新为新的尾节点
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
} //直接调用AbstractQueuedSynchronizer.compareAndSetTail
//原子更新链表的尾节点
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
} //直接调用AbstractQueuedSynchronizer.enq
//使用CAS原子性将节点插入到链表尾部
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
//当没有初始化时,进行头结点与尾节点初始化,成功后,将头结点与尾节点指向同一个处对象
if (compareAndSetHead(new Node()))
tail = head;
} else {
//将节点插入到链表尾部 //设置当前节点的前一个节点为当前链表的尾节点
node.prev = t;
//当CAS尝试更新尾节点成功,则将新尾节点的前一个节点的下一个节点更新为新的尾节点
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
} //直接调用AbstractQueuedSynchronizer.acquireQueued
//当当前节点为等待队列中的第一个节点时,获取到锁
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//当当前节点的前节点为头结点,并为当前线程尝试获取锁,成功时,设置当前节点为头结点,并将当前节点的前节点的下一个节点取消,使之没有被引用,这样可以被GC回收
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//设置当前节点的前节点状态为唤醒状态,且将当前节点挂起
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
} //直接调用AbstractQueuedSynchronizer.setHead
//设置当前节点为头结点,并将当前节点的前节点置为空,以及将当前节点持有的线程设置为空
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
} //直接调用AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
//当当前节点的前节点状态为唤醒时,则表明下一个被执行的节点是当前节点,即线程会被唤醒
return true;
if (ws > 0) {
//当当前节点的前节点为取消的节点,则往前查找一个非取消的节点
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
//将查找的非取消节点的下一个节点更新为当前节点
pred.next = node;
} else {
//尝试将当前节点的前节点的状态设置为唤醒状态
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
} //直接调用AbstractQueuedSynchronizer.compareAndSetWaitStatus
//尝试更新节点的状态
private static final boolean compareAndSetWaitStatus(Node node, int expect, int update) {
return unsafe.compareAndSwapInt(node, waitStatusOffset, expect, update);
} //直接调用AbstractQueuedSynchronizer.parkAndCheckInterrupt
//将当前线程挂起
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
Semaphore:资源控制,是线程同步的工具类,默认是公平策略;如停车场,最多只有100个车位,故当满时,只能出一个之后,才能进一个;
当新建时,需指定控制的资源的个数,即同时进行的线程数,还有就是使用的是公平还是非公平的策略,默认是非公平;
acquire获取资源;
release释放资源; CountDownLatch:满足条件时触发;如赛跑,在开始前,所有运动员准备好之后,才能开始;当所有运动员到达终点时,才能结束;还如,当获取专辑的所有视频资源信息时,只有所有的视频信息获取到之后,才能进行之后的操作;当新建时,需要指定锁计数的个数; await:等待锁计数为0,之前一直阻塞
countDown:递减计数器的计数,如果计数达到0,则释放所有等待的线程; 实际操作都是由其属性Sync来进行,且该属性对象继承了AbstractQueuedSynchronizer类; 参考资料:
http://www.itzhai.com/the-introduction-and-use-of-a-countdownlatch.html
http://mouselearnjava.iteye.com/blog/1921468
ReentrantLock源码了解的更多相关文章
- Java并发系列[5]----ReentrantLock源码分析
在Java5.0之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile.我们知道synchronized关键字实现了内置锁,而volatile关键字保证了多线程的内存可 ...
- Java并发编程笔记之ReentrantLock源码分析
ReentrantLock是可重入的独占锁,同时只能有一个线程可以获取该锁,其他获取该锁的线程会被阻塞后放入该锁的AQS阻塞队列里面. 首先我们先看一下ReentrantLock的类图结构,如下图所示 ...
- Java并发编程-ReentrantLock源码分析
一.前言 在分析了 AbstractQueuedSynchronier 源码后,接着分析ReentrantLock源码,其实在 AbstractQueuedSynchronizer 的分析中,已经提到 ...
- 第六章 ReentrantLock源码解析2--释放锁unlock()
最常用的方式: int a = 12; //注意:通常情况下,这个会设置成一个类变量,比如说Segement中的段锁与copyOnWriteArrayList中的全局锁 final Reentrant ...
- java多线程---ReentrantLock源码分析
ReentrantLock源码分析 基础知识复习 synchronized和lock的区别 synchronized是非公平锁,无法保证线程按照申请锁的顺序获得锁,而Lock锁提供了可选参数,可以配置 ...
- ReentrantLock源码分析--jdk1.8
JDK1.8 ArrayList源码分析--jdk1.8LinkedList源码分析--jdk1.8HashMap源码分析--jdk1.8AQS源码分析--jdk1.8ReentrantLock源码分 ...
- 死磕 java同步系列之ReentrantLock源码解析(二)——条件锁
问题 (1)条件锁是什么? (2)条件锁适用于什么场景? (3)条件锁的await()是在其它线程signal()的时候唤醒的吗? 简介 条件锁,是指在获取锁之后发现当前业务场景自己无法处理,而需要等 ...
- JUC AQS ReentrantLock源码分析
警告⚠️:本文耗时很长,先做好心理准备,建议PC端浏览器浏览效果更佳. Java的内置锁一直都是备受争议的,在JDK1.6之前,synchronized这个重量级锁其性能一直都是较为低下,虽然在1.6 ...
- java源码-ReentrantLock源码分析-1
ReentrantLock 继承于lock是比较常用的独占锁,接下来我们来分析一下ReentrantLock源码以及接口设计: Sync是ReentrantLock的内部静态抽象类继承Abstract ...
- ReentrantLock 源码分析从入门到入土
回答一个问题 在开始本篇文章的内容讲述前,先来回答我一个问题,为什么 JDK 提供一个 synchronized 关键字之后还要提供一个 Lock 锁,这不是多此一举吗?难道 JDK 设计人员都是沙雕 ...
随机推荐
- python 喜马拉雅 音乐下载 演示代码
1.主程序文件 import os import json import requests from contextlib import closing from progressbar import ...
- jquery的几种ajax方式对比
jquery的几种ajax方式对比 jquery的ajax方式有如下几种: 1. $.post(url,params,callback); 2. $.getJSON(url,params,ca ...
- Web安全测试-WebScarab
[功能] WebScarab是一个用来分析使用HTTP和HTTPS协议的应用程序框架.其原理很简单,WebScarab可以记录它检测到的会话内容(请求和应答),并允许使用者可以通过多种形式来查看记录. ...
- springboot整合rabbirmq(3.7.9)中使用mandatory参数获取匹配失败的消息以及存入rabbitmq备份交换器中!
先说下这个参数的作用: /** * Mandatory为true时,消息通过交换器无法匹配到队列会返回给生产者 * 为false时,匹配不到会直接被丢弃 */在一些特定场景下还是有用处的!接下来说一下 ...
- Android 7.0 新增功能和api
Android 7.0 Nougat 为用户和开发者引入多种新功能.本文重点介绍面向开发者的新功能. 请务必查阅 Android 7.0 行为变更以了解平台变更可能影响您的应用的领域. 要详细了解 A ...
- express-partials使用方法
1.安装express-partials 方法一:运行cmd用npm install express-partials 方法二:在package.json里面的dependencies添加" ...
- CSS之:active选择器
Active的一段话 active的英文解释为“积极的”,表现在鼠标上就是点击的意思.关于active选择器最多的示例恐怕就是应用在链接上面的,然而打开链接是一个一瞬间的动作,这不能很好的体现acti ...
- ExpressMapper- The New .NET Mapper!
推荐,据测试比手工映射的效率还高. https://www.codeproject.com/Tips/1009198/Expressmapper-The-New-NET-Mapper
- .NetCore 分布式日志收集Exceptionless 在Windows下本地安装部署及应用实例
自己安装时候遇到很多问题,接下来把这些问题写出来希望对大家有所帮助 搭建环境: 1.下载安装 java 8 SDK (不要安装最新的10.0) 并配置好环境变量(环境变量的配置就不做介绍了) 2.下载 ...
- Z字形变换
将字符串 "PAYPALISHIRING" 以 Z 字形排列成给定的行数: P A H N A P L S I I G Y I R 之后从左往右,逐行读取字符:"PAHN ...