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源码了解的更多相关文章

  1. Java并发系列[5]----ReentrantLock源码分析

    在Java5.0之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile.我们知道synchronized关键字实现了内置锁,而volatile关键字保证了多线程的内存可 ...

  2. Java并发编程笔记之ReentrantLock源码分析

    ReentrantLock是可重入的独占锁,同时只能有一个线程可以获取该锁,其他获取该锁的线程会被阻塞后放入该锁的AQS阻塞队列里面. 首先我们先看一下ReentrantLock的类图结构,如下图所示 ...

  3. Java并发编程-ReentrantLock源码分析

    一.前言 在分析了 AbstractQueuedSynchronier 源码后,接着分析ReentrantLock源码,其实在 AbstractQueuedSynchronizer 的分析中,已经提到 ...

  4. 第六章 ReentrantLock源码解析2--释放锁unlock()

    最常用的方式: int a = 12; //注意:通常情况下,这个会设置成一个类变量,比如说Segement中的段锁与copyOnWriteArrayList中的全局锁 final Reentrant ...

  5. java多线程---ReentrantLock源码分析

    ReentrantLock源码分析 基础知识复习 synchronized和lock的区别 synchronized是非公平锁,无法保证线程按照申请锁的顺序获得锁,而Lock锁提供了可选参数,可以配置 ...

  6. ReentrantLock源码分析--jdk1.8

    JDK1.8 ArrayList源码分析--jdk1.8LinkedList源码分析--jdk1.8HashMap源码分析--jdk1.8AQS源码分析--jdk1.8ReentrantLock源码分 ...

  7. 死磕 java同步系列之ReentrantLock源码解析(二)——条件锁

    问题 (1)条件锁是什么? (2)条件锁适用于什么场景? (3)条件锁的await()是在其它线程signal()的时候唤醒的吗? 简介 条件锁,是指在获取锁之后发现当前业务场景自己无法处理,而需要等 ...

  8. JUC AQS ReentrantLock源码分析

    警告⚠️:本文耗时很长,先做好心理准备,建议PC端浏览器浏览效果更佳. Java的内置锁一直都是备受争议的,在JDK1.6之前,synchronized这个重量级锁其性能一直都是较为低下,虽然在1.6 ...

  9. java源码-ReentrantLock源码分析-1

    ReentrantLock 继承于lock是比较常用的独占锁,接下来我们来分析一下ReentrantLock源码以及接口设计: Sync是ReentrantLock的内部静态抽象类继承Abstract ...

  10. ReentrantLock 源码分析从入门到入土

    回答一个问题 在开始本篇文章的内容讲述前,先来回答我一个问题,为什么 JDK 提供一个 synchronized 关键字之后还要提供一个 Lock 锁,这不是多此一举吗?难道 JDK 设计人员都是沙雕 ...

随机推荐

  1. bzoj千题计划230:bzoj3205: [Apio2013]机器人

    http://www.lydsy.com/JudgeOnline/problem.php?id=3205 历时一天,老子终于把它A了 哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈 因为不懂spfa ...

  2. Java入门系列(八)多线程

    基本线程类指的是Thread类,Runnable接口,Callable接口 典型多线程问题 生产者-消费者 死锁问题

  3. 详谈ASP.NET的DataReader对象

    最近频繁用到了DataReader这个对象,其实对于DataReader,之前也用到过,说实话我个人觉得很不好懂.相比之下觉得DataSet对象好用的多,但是有时取出的数据不需要很多的时候,DataR ...

  4. [游戏数据分析]WAU模型简介及WAU预测

    声明:本博客中所采用的数据并非真实数据,会对真实数据加以变换,重在讨论游戏数据分析的思路. 这里是参考友盟的WAU模型[文章网址,  演示网址],利用某款游戏(以下称为游戏A)数据进行的分析. 作用: ...

  5. 【Udacity并行计算课程笔记】- lesson 1 The GPU Programming Model

    一.传统的提高计算速度的方法 faster clocks (设置更快的时钟) more work over per clock cycle(每个时钟周期做更多的工作) more processors( ...

  6. java中final、finally、finalized使用方法

    首先需要明白  final和finally是关键字,finalize是一个方法. 1. final关键字 final可以修饰类.方法.变量, 修饰类表示类不可以被继承 修饰方法表示此方法不可以被重写( ...

  7. spring事务详解(一)初探讨

    一.什么是事务 维基百科:数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成.理解:事务(Transaction)是数据库区别于文件系统的重要特性之一.传 ...

  8. innobackupex做MySQL增量备份及恢复【转】

    创建备份用户 mysql> grant process,reload,lock tables,replication client on *.* to 'backup'@'localhost' ...

  9. vue 兼容IE报错解决方案

    IE 页面空白 报错信息 此时页面一片空白 报错原因 Babel 默认只转换新的 JavaScript 语法(如箭头函数),而不转换新的 API ,比如 Iterator.Generator.Set. ...

  10. 卷积神经网络CNN经典模型整理Lenet,Alexnet,Googlenet,VGG,Deep Residual Learning(转)

    参考:http://blog.csdn.net/xbinworld/article/details/45619685