分析NonfairSync加锁/解锁过程
类继承关系:
NonfairSync => Sync => AbstractQueuedSynchronizer
类NonfairSync
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
分析:
compareAndSetState(0, 1):通过cas操作更新state状态,若成功,则获取到锁,否则,进行排队申请操作acquire
类AbstractQueuedSynchronizer
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
分析:
tryAcquire方法最终实现为:
类Sync
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;
}
通过cas操作更新state状态,若成功,则获取到锁,否则,首先进行排队,
类AbstractQueuedSynchronizer
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) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
注意:
每一个线程被封装成一个Node节点。
进入enq方法——入队列
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;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
分析:
这里针对head和tail属性的赋值均为cas原子操作。
最终模型如图:
如果多线程并发入队,最终结果如图:
注:prev和next分别为Node类的两个属性
入队操作结束后,开始请求队列acquireQueued
类AbstractQueuedSynchronizer
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
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);
}
}
分析:
- Node p = node.predecessor()获取node的prev节点
- 如果prev==head节点并且tryAcquire返回true,则更新head节点为当前节点,并退出循环,也就获取到了锁。
- 否则的话,执行shouldParkAfterFailedAcquire和parkAndCheckInterrupt。
- setHead方法来修改head属性,改变队列的头部节点
其中,shouldParkAfterFailedAcquire方法是针对waitStatus属性的修改
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
LockSupport.park(this),使当前线程进入阻塞。
unlock分析
类AbstractQueuedSynchronizer
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
类Sync
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;
}
分析:
主要工作就是设置还原state状态
类AbstractQueuedSynchronizer
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);
}
分析:
- 入参node即head节点
- Node s = node.next;即获取到下一个节点,即正在阻塞中的线程
- LockSupport.unpark(s.thread);即激活线程
分析NonfairSync加锁/解锁过程的更多相关文章
- 从ReentrantLock加锁解锁角度分析AQS
本文用于记录在学习AQS时,以ReentrantLock为切入点,深入源码分析ReentrantLock的加锁和解锁过程. 同步器AQS的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理 ...
- 进程间通信(IPC)+进程加锁解锁
[0]README 0.1) source code and text description are from orange's implemention of a os: 0.2) for com ...
- Spring Ioc源码分析系列--Bean实例化过程(一)
Spring Ioc源码分析系列--Bean实例化过程(一) 前言 上一篇文章Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理已经完成了对 ...
- 多线程与高并发(二)—— Synchronized 加锁解锁流程
前言 上篇主要对 Synchronized 的锁实现原理 Monitor 机制进行了介绍,由于 Monitor 基于操作系统调用,上下文切换导致开销大,在竞争不激烈时性能不算很好, 在 jdk6 之后 ...
- Linux第三周——跟踪分析内核的启动过程
跟踪分析内核的启动过程实验 张潇月<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 这周主要学习的是对内核 ...
- Linux 进程与线程四(加锁--解锁)
线程共享进程的内存空间,打开的文件描述符,全局变量. 当有多个线程同事访问一块内存空间或者一个变量.一个文件描述符,如果不加控制,那么可能会出现意想不到的结果. 原子操作 对于我们的高级语言(C语言, ...
- 使用redis的比较完美的加锁解锁
使用redis的比较完美的加锁解锁 tags:redis read&write redis加锁和解锁 php 习惯性说一下写这篇文章要说明什么,我们经常用redis进行加锁操作,目的是为了解决 ...
- MyBatis 源码分析 - SQL 的执行过程
* 本文速览 本篇文章较为详细的介绍了 MyBatis 执行 SQL 的过程.该过程本身比较复杂,牵涉到的技术点比较多.包括但不限于 Mapper 接口代理类的生成.接口方法的解析.SQL 语句的解析 ...
- MyBatis 源码分析 - 映射文件解析过程
1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...
随机推荐
- MarkDown总结
转载自:https://www.jianshu.com/p/q81RER:做了一些部分修改: 一:总纲 1.所有的格式均要空格,如#后面加个空格,-后面加空格,1.后面加空格等等: 2.双向包裹的不能 ...
- Linux下VNC配置使用总结:开启+桌面配置+安全访问
操作环境:CentOS 5.3 + Windows XP SP3 32bit + RealVNC 4.1.2 i386 + TigerVNC. 参考:潇湘隐者-Linux系统VNC配置实践总结,萨米的 ...
- C和C++中的volatile、内存屏障和CPU缓存一致性协议MESI
目录 1. 前言2 2. 结论2 3. volatile应用场景3 4. 内存屏障(Memory Barrier)4 5. setjmp和longjmp4 1) 结果1(非优化编译:g++ -g -o ...
- 【1】C#文件操作之目录操作
命名空间:System.IO Directory类下的静态方法: 一.创建目录 Directory.CreateDirectory(string fileName); 二.获取指定目录下的所有文件名 ...
- rpcbind.service启动失败
新装的服务器,启动rpcbind.service通常失败,执行下面的两个命令经常卡死,一直不返回,也不报错 #systemctl start nfs-server.service #systemctl ...
- bat文件命令
- 20155326 第十周课下作业-IPC
20155326 第十周课下作业-IPC 学习题目: 研究Linux下IPC机制:原理,优缺点,每种机制至少给一个示例,提交研究博客的链接 共享内存 管道 FIFO 信号 消息队列 学习过程 -IPC ...
- EBS中 EXCEL 格式报表输出的公用API
http://blog.itpub.net/10359218/viewspace-752601/ 最近的项目上写了一个公用的API,很久以前就用EXCEL发布过报表,但从没想过写API来简化... ...
- 三维数组—— 与宝玉QQ群交流 之三
鞠老师 12:50:34 A[excel文件名][excel.sheet][sheet.行][sheet.列] 构成四维数组 计131-张振渊 12:51:54 a[1][0][0][3]? 鞠老师 ...
- Python 学习第三部分函数——第一章函数基础
函数是python 为了代码最大程度的重用和最小代码冗余而提供的最基本的程序结构.使用它我们可以将复杂的系统分解为可管理的部件. 函数相关语句 def... 创建一个对象并将其赋值给 ...