Java Lock (AQS)

Lock vs Synchorized

Lock

  • 超时获取
  • 可中断获取
  • 非阻塞获取

AbstractQueuedSynchronizer

Lock 的实现基础,实现了基于队列的锁的等待。竞争锁失败的线程会进去队列中等待被唤醒重新竞争锁。

acquire/release

以对一个int 值做CAS 操作来实现锁的获取和释放

private volatile int state;

AQS准备了一下模板方法供子类实现
独享锁,同时只有一个线程获取

boolean tryAcquire(int arg);
boolean tryRelease(int arg);

共享锁

int tryAcquireShared(int arg)
boolean tryReleaseShared(int arg

队列

双向队列,shuang每个node记录了对应的queue

private transient volatile Node head;
private transient volatile Node tail;
class Node {
  volatile int waitStatus;
  volatile Node prev;
  volatile Node next;
  volatile Thread thread;
}

Node Status 有以下几种

- 0 初始状态
- CANCELLED = 1
- SINAL = -1 后面的Node在等待唤醒
- PROGAGATE = -2 需要再继续唤醒后面的Node (shared lock用)
- CONDITION = -3 condition用的node

Exclusive Mode

Acquire

  1. 先尝试 tryAcquire。若成功则获取锁,失败则进入队列。
  2. 创建Node, 自旋CAS加入队尾
  3. 改变前一个 Node 的状态为 Signal。如果前一个的状态为 CANCELLED, 则跳过前一个直到前一个node 为非CANCELLED 状态。
  4. 如果是当前队列的第二个node, 则再尝试获取锁 tryAcquire, 以免加入队列完成前,前一个node刚好被释放
  5. park 当前线程

Release

  1. 尝试 tryRelease,成功后,unpark 下一个node 对应的线程。
  2. 被唤醒的线程检查 tryAcquire 获取锁,并将 Head 设置为自己。

Shared Mode

Shared mode 获取锁的部分和 Exclusive mode 是一样的,对于state 和队列的CAS操作保证了在多个线程同时获取锁的情况下的正确性。
但是释放锁的时候,却可能出现多个线程同时释放的情况。所以就需要能将多重释放在队列里传递下去,唤醒尽可能多的线程。

Acquire

与独占模式基本一样

Release

释放锁的线程

  1. tryReleaseShared 成功后
  2. 如果 head.watiStatus == SIGNALreset head.watiStatus = 0, 唤醒下一个node
  3. 否则如果 head.waitStatus == 0, set head.waitStatus = PROPAGATE
  4. 如果发现 head 已经改变则重复2,3

被唤醒的线程

  1. tryAcquireShared 获取锁。
  2. 设置自己为新的head, 当tryAcquireShared 返回>0 或者 旧的head 和新的head 任意一个状态 < 0 (为SIGNAL/PROPAGATE)时。则继续尝试唤醒下一个node。

这样我们保障了

  1. 如果tryAcquireShared > 0, 直接说明可以获取多个资源,则尝试继续释放。
  2. 如果tryAcquireShared == 0,但是接下来有释放,则旧head.status = PROPAGATE,会尝试继续释放。

利用AQS的实现

ReentrantLock

tryAcquire 的时候,检查持有锁的是否为当前线程,是的话,则 state + 1,获取锁。
其他线程只有在 state == 0 的时候才能获取锁。

Fair/Unfair

在锁被释放的时候,可能存在其他线程和队列中的线程竞争锁的情况。

  • Fair 遵守先来后到,让队列中的线程先获取锁。
  • Unfair 共同竞争,大概率会出现刚释放锁的线程又再次获得锁。高吞度量

实现

Fair 版本在获取锁的时候,检查队列中是否有排在自己之前的node, 且线程不为自己。

ReentrantReadWriteLock

同一个state, 用高位代表读,低位代表写

Read Lock

shared mode 用来获取/释放读锁,如果当前

  • 没有写锁
  • 当前有写锁,但处在同一线程

则可以获取

Write Lock

Exclusive mode 用来获取/释放写锁。可重入。

利用AQS 巧妙地实现了 write lock 对于read lock 的屏障作用。当read lock释放唤醒write lock的线程时,由于write node 处于独占模式,不会讲释放propagate 给接下来的node。

write lock 降级

允许下列操作

write.lock()
read.lock()
write.unlock()
read.unlock

Condition

Lock.newCondition().await()/signal() 对应 Object.wait()/notify()
不同的是 condition 可以

  • 同一个lock 可以有多个 condition.
  • sinal 会唤醒等待最久的线程,notify 则是随机唤醒。
  • 可以选择不响应中断

ConditionObject

condition 队列, 复用AQS的node

private transient Node firstWaiter;
private transient Node lastWaiter;

await

  1. 生成node,node.ws = CONDITION, 放入condition 队列。
  2. 释放锁。park 住当前线程等待被释放。

signal

  1. reset node.ws = 0
  2. 将condition 队首的node 移动到对应 lock 的 AQS 队列的末尾。
  3. unpark 该线程,线程检查自己已经处于AQS队列,重新进入 AQS 的等待程序。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容

  • ReentrantLock 介绍 一个可重入的互斥锁,它具有与使用{synchronized}方法和语句访问的隐式...
    tomas家的小拨浪鼓阅读 4,034评论 1 4
  • 前言 上一篇文章《基于CAS操作的Java非阻塞同步机制》 分析了非同步阻塞机制的实现原理,本篇将分析一种以非同步...
    Mars_M阅读 4,784评论 5 9
  • 本篇不写前言,直接扒衣服! 1:concurrent包结构 最底层: volatile变量:volatile保证变...
    后厂村老司机阅读 1,537评论 0 3
  • 感赏刚才自己写了这么长一篇舒缓文,写完之后真的感觉好了太多。 静怡姐说过,爱自己就是要陪伴自己,尤其自己发现:...
    猫公主喵阅读 280评论 0 0
  • 晚上陪着家人看叶问3,张天志和叶问为了咏春正宗大打出手,说起来都是梁赞的徒孙,可是争的就是一个高脚咏春和低脚永春到...
    武哥a阅读 1,268评论 0 2