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 设计人员都是沙雕 ...
随机推荐
- bzoj千题计划230:bzoj3205: [Apio2013]机器人
http://www.lydsy.com/JudgeOnline/problem.php?id=3205 历时一天,老子终于把它A了 哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈 因为不懂spfa ...
- Java入门系列(八)多线程
基本线程类指的是Thread类,Runnable接口,Callable接口 典型多线程问题 生产者-消费者 死锁问题
- 详谈ASP.NET的DataReader对象
最近频繁用到了DataReader这个对象,其实对于DataReader,之前也用到过,说实话我个人觉得很不好懂.相比之下觉得DataSet对象好用的多,但是有时取出的数据不需要很多的时候,DataR ...
- [游戏数据分析]WAU模型简介及WAU预测
声明:本博客中所采用的数据并非真实数据,会对真实数据加以变换,重在讨论游戏数据分析的思路. 这里是参考友盟的WAU模型[文章网址, 演示网址],利用某款游戏(以下称为游戏A)数据进行的分析. 作用: ...
- 【Udacity并行计算课程笔记】- lesson 1 The GPU Programming Model
一.传统的提高计算速度的方法 faster clocks (设置更快的时钟) more work over per clock cycle(每个时钟周期做更多的工作) more processors( ...
- java中final、finally、finalized使用方法
首先需要明白 final和finally是关键字,finalize是一个方法. 1. final关键字 final可以修饰类.方法.变量, 修饰类表示类不可以被继承 修饰方法表示此方法不可以被重写( ...
- spring事务详解(一)初探讨
一.什么是事务 维基百科:数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成.理解:事务(Transaction)是数据库区别于文件系统的重要特性之一.传 ...
- innobackupex做MySQL增量备份及恢复【转】
创建备份用户 mysql> grant process,reload,lock tables,replication client on *.* to 'backup'@'localhost' ...
- vue 兼容IE报错解决方案
IE 页面空白 报错信息 此时页面一片空白 报错原因 Babel 默认只转换新的 JavaScript 语法(如箭头函数),而不转换新的 API ,比如 Iterator.Generator.Set. ...
- 卷积神经网络CNN经典模型整理Lenet,Alexnet,Googlenet,VGG,Deep Residual Learning(转)
参考:http://blog.csdn.net/xbinworld/article/details/45619685