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. git安装与初始化

    命令行 Git有多重方式使用 原生命令行,才能使用git所有命令,会git命令再去用gui图形工具,完全无压力 GUI图形软件,只是实现了git的部分功能,以减免操作难度,难以记住git原生命令 不同 ...

  2. SHELL (2) —— Shell变量的核心基础知识和实践

    摘自:Oldboy Linux运维——SHELL编程实战 Shell变量:用一个固定的字符串(也可能是字符.数字等的组合)代替更多.更复杂的内容,该内容里可能还会包含变量.路径.字符串等其它的内容. ...

  3. mysql先排序在分组

    – 表的结构 `test`– CREATE TABLE IF NOT EXISTS `test` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varcha ...

  4. [转]GCC系列: __attribute__((visibility("")))

    在 objc-api.h 里面有很多关于__attribute__ 的定义. 例如 #if !defined(OBJC_VISIBLE) # if TARGET_OS_WIN32 # if defin ...

  5. mybatis输入输出映射——(五)

    0.#{}与${}区别 #{}实现的是向prepareStatement中的预处理语句中设置参数值,sql语句中#{}表示一个占位符即?. <!-- 根据id查询用户信息 --> < ...

  6. Longest Words

    Given a dictionary, find all of the longest words in the dictionary. Example Given { "dog" ...

  7. phantomjs waitFor

    function waitFor(testFx, onReady, timeOutMillis) { var maxtimeOutMillis = timeOutMillis ? timeOutMil ...

  8. 如何将java项目转化为web项目

    1.修改工程文件 找到项目工作空间目录,打开.project文件,找到:<natures> </natures>代码段,在代码段中加入如下内容并保存:<nature> ...

  9. Fiddler实现移动端手机抓包

    Fiddler是一个http调试代理,它能 够记录所有的你电脑和互联网之间的http通讯,Fiddler 可以也可以让你检查所有的http通讯,设置断点,以及Fiddle 所有的“进出”的数据(指co ...

  10. 中文分词-jieba

    支持三种分词模式: 精确模式,试图将句子最精确地切开,适合文本分析: 全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义: 搜索引擎模式,在精确模式的基础上,对长词再次切分 ...