分析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 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...
随机推荐
- celery 4.1下报kombu.exceptions.EncodeError: Object of type 'bytes' is not JSON serializable 处理方式
#python代码如下 from celery import Celeryimport subprocess app = Celery('tasks', broker='redis://localho ...
- create table b1 as select * from b建表锁表测试
A: create table a1 like a; insert into a1 as select * from a; B: create table b1 as select * from b; ...
- Mapnik
Downloads Latest Release The latest release is Mapnik v3.0.22.最新版本是Mapnik v3.0.22. Mapnik 3.0.22 Rel ...
- ArrayBlockingQueue和LinkedBlockingQueue
1.BlockingQueue接口定义了一种阻塞的FIFO queue ArrayBlockingQueue和LinkedBlockingQueue的区别: 1. 队列中锁的实现不同 ArrayBlo ...
- Leetcode--680. Valid Palindrome II(easy)
Given a non-empty string s, you may delete at most one character. Judge whether you can make it a pa ...
- Python自动化开发 - select模块
介绍: IO-多路复用:监听多个socker对象是否有变化,包括可读.可写.发送错误 Python中的select模块专注于I/O多路复用,提供了select poll epoll三个方法(其中后两个 ...
- CentOS 7安装fail2ban+Firewalld防止SSH爆破与CC攻击
准备工作 1.检查Firewalld是否启用 #如果您已经安装iptables建议先关闭 service iptables stop #查看Firewalld状态 firewall-cmd --sta ...
- Django:全文检索功能可参考博客
https://blog.csdn.net/AC_hell/article/details/52875927 https://www.zmrenwu.com/courses/django-blog-t ...
- PS插件CameraRaw-HSL色彩模式
一.HSL百度百科 HSL色彩模式是工业界的一种颜色标准,是通过对色相(H).饱和度(S).明度(L)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,HSL即是代表色相,饱和度,明度三 ...
- MariaDB 库的基本操作(2)
MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可MariaDB的目的是完全兼容MySQL,包括API和命令行,MySQL由于现在闭源了,而能轻松成为MySQ ...