美文网首页
AQS和它的实现类

AQS和它的实现类

作者: 张大头不编程 | 来源:发表于2019-12-02 21:29 被阅读0次

1.AQS(抽象队列同步器)

1.1 类图

1.2 Node

两种模式:独占、共享。

waitStatus 有5种:

0 // 默认值

CANCELED= 1 // 线程已被取消

SIGNAL= -1 // 表示后继节点需要被唤醒

CONDITION= -2 // 表示该节点在条件队列等待

PROPAGATE= -3 // 表示下个共享节点aquire时无条件的传播

1.3 CLH

condition.await 时,会将线程加入到等待队列中,并且释放锁,然后将线程挂起。线程被唤醒后会判断线程是否中断并清除中断标记,若signal之前就中断了则抛InterruptedException,否则执行下中断。

condition.signal或signalAll 会将等待队列的头节点或所有节点转移到同步队列中。

1.4 总结思考

1.4.1 为啥同步队列都是从后向前遍历?

由cancelAcquire() 、unparkSuccessor()可知,

因为同步队列中,任何一个节点都有可能被中断,被中断的Node 会被标记成CANCELED。Node的next指针指向自己。并发场景这个Node要去唤醒后继节点,并发被取消了。如果用next 遍历就死循环了,但是pred 倒着遍历不会有问题,因为pred指针一直没变。

2.ReentrantLock(重入锁)

2.1 作用

控制资源竞争。

2.2 核心思想

1. AQS的state 存储重入次数,state=0 时可获取锁.

2. 独占线程重入时,state自增;释放时,state自减。

3. 默认非公平锁,aquire 时:

    3.1 非公平,

    state=0时,cas获取锁,若获取失败线程会进入同步队列,并挂起、唤醒后继续自旋。

    3.2 公平,

    state=0时,cas成功且没有前驱节点才获取锁,若获取失败线程会进入同步队列,并挂起、继续自旋。

4. release 时,当state=0,唤醒队头线程,重新竞争。

2.3 核心方法

lock()

unlock()

2.4 图解

3.CountDownLatch(倒数闭锁)

3.1 作用

若线程A依赖线程B和线程C的执行,控制A在B、C 都执行完后再执行A。

3.2 核心思想

1. AQS的state存储需要被countdown次数,。

2. 主线程await()时,若state>0,则主线程挂起。

3. 其他线程downtdown()时,state减1,若state=0,则主线程取消挂起。

3.3 核心方法

await()

countDown()

3.4 图解

4.Semaphore(信号量)

4.1 作用

流量控制。

4.2 核心思想

1. AQS的state 存储许可数,state>0 时可获得许可证。

2. 共享模式,不需要判断独占线程。

3. 默认非公平,获取时:

    3.1 非公平,

    state>0时,cas获取许可,若获取失败线程会进入同步队列,并挂起、唤醒后继续自旋。

    3.2 公平,

    state>0时,cas成功且没有前驱节点才获取许可,若获取失败线程会进入同步队列,并挂起、继续自旋。

4. 释放,自旋直到释放成功,唤醒队头线程,重新竞争。

4.3 核心方法

aquire()

release()

4.4 图解

5.ReentrantReadWriteLock(读写锁)

5.1 作用

控制资源竞争。

5.2 核心思想

1. AQS的state 存储读锁和写锁的state。

如 state=.0000 0000 0000 0011 0000 0000 0000 0001

高16位代表读锁state,低16位代表写锁state。

2. 写锁是独占模式,读锁是共享模式。读锁和写锁不能同时拥有。只能拥有一个写锁,或多个读锁。

3. 读锁:

aquire 时,

    1. 若有其他线程有写锁,则获取失败。

    2.

        a.公平:队列里没有前驱节点,且读锁state没有超过最大值,且读锁state cas成功。

        b.非公平:队列里没有独占节点,且读锁state没有超过最大值,且读锁state cas成功。

    若不满足a/b,则会完整的重试一次aquire。

    3. 若是第一次获取读锁,则特殊记录一下线程和该线程的读锁state;否则threadLoacl记录当前线程的读锁state。

    4. 获取成功。

release 时,

    1. 若是第一次获取读锁,则清空第一次的特殊记录。否则,threadLocal记录的state减一,减完则清空。

    2. 自旋直到cas 读锁state-1。

    3. 当读锁state=0时,唤醒头结点的线程,重新竞争。

  写锁

aquire时,和reentrant类似,有略微差异,

    1. 若有写锁,或者当前线程不是独占线程,则获取失败。

release时,

    1. 若是独占线程,且写锁的state=0,则唤醒队列的头结点线程,重新竞争。

5.3 核心方法

rwl.readLock().lock()

rwl.readLock.unlock()

rwl.writeLock.lock()

rwl.writeLock.unlock()

5.4 图解

5.5 总结思考

在读多写少的场景下,读写锁可能会造成写饥饿。JDK1.8 引入了StampedLock,加了个乐观锁,

当读和写冲突时,不是阻塞写,而是重读不阻塞写。

相关文章

  • AQS和它的实现类

    1.AQS(抽象队列同步器) 1.1 类图 1.2 Node 两种模式:独占、共享。 waitStatus 有5种...

  • ReentrantLock原理学习

    基于AbstractQueuedSynchronizer(简称AQS)这个抽象类实现,AQS实现了主体逻辑,使用的...

  • LockSupport.park(...)

    AQS是java.conccurent包下诸多工具类的抽象基类,借以AQS抽象基类,实现如lock,unlock,...

  • 1.8AQS(AbstractQueuedSynchronize

    AQS类继承关系图 因为AQS类本身都是空方法,要使用必须自己去继承实现 AbstractOwnableSynch...

  • 多线程(11) — AQS抽象队列同步

    AQS是指AbstractQueuedSynchronizer,抽象队列同步。AQS是多个重要接口实现的工具类包括...

  • AQS在jdk中的应用

    上篇文章我们详细分析了AQS的底层实现原理,这节就来探索jdk中使用AQS实现的工具类 从源码看AQS[https...

  • JAVA之AQS

    AQS的概述 AbstractQueuedSynchronizer简述为AQS,是一个抽象队列锁类,通过继承实现A...

  • AQS(抽象队列同步器)源码初窥

    本质 java锁的实现原理就是继承AQS抽象类java锁继承Lock接口, 而Lock需要实现的接口依赖AQS的实...

  • ReentrantLock 核心源码解析

    学习完 AQS,本文我们就来研究第一个 AQS 的实现类:ReentrantLock。 1 基本设计 Reentr...

  • JDK源码分析之ReentrantLock

    ReentrantLock可重入锁基于AQS类实现。AQS提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器...

网友评论

      本文标题:AQS和它的实现类

      本文链接:https://www.haomeiwen.com/subject/ooxqgctx.html