欢迎您的访问
专注于Java技术系列文章的Java技术分享网站
关注我们

—— 加入社群 ——
「十大城市」工作内推
「微信/QQ」技术讨论
「面试真题」随时领取
公众号名称:搜云库技术团队  公众号ID:souyunku
关注公众号后发送 口令 获取关闭弹窗地址
公众号每天中午 12:20 为你推送一篇技术文章

多线程笔记 四:AQS-AbstractQueuedSynchronizer

号外:限时领取:2020,全网最新、最全的技术知识体系

什么是AbstractQueuedSynchronizer?为什么我们要分析它?

AQS:抽象队列同步器,原理是:当多个线程去获取锁的时候,如果获取锁失败了,当前线程就会被打包成一个node节点放入同步队列里面使用LockSuport的park方法阻塞起来,如果有线程释放了锁,放入同步队列的线程就会被LockSupport的unpark方法唤醒再次去获取锁,如果获取锁又失败了就再次打包成node节点放入同步队列,这样不断的循环直到拿到锁

为什么药分析AQS:因为线程都是基于AQS实现的

AQS的基本使用方法

同步器的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态。可重写的方法有:

tryAcquire 独占锁获取

tryRelease 独占锁释放

tryAcquireShared 共享锁获取

tryReleaseShared 共享锁释放

isHeldExclusively 快速判断被线程独占

同步器的设计是基于模板方法模式, 使用者需要继承同步器并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法将会调用使用者重写的方法。

对同步状态进行更改,这时就需要使用同步器提供的3个方法

getState() 获取同步状态

setState 设置同步状态

compareAndSetState 原子的设置同步状态来进行操作。

什么是LockSupport

LockSupport定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能,而LockSupport也成为构建同步组件的基础工具。LockSupport定义了一组以park开头的方法用来阻塞当前线程,以及unpark(Thread thread)方法来唤醒一个被阻塞的线程。简单第说:LockSupport就是用来和AQS配合阻塞和唤醒线程的

同步队列

抽象队列同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成为一个节点(Node)并将其加入同步队列。同步器拥有首节点(head)和尾节点(tail),没有成功获取同步状态的线程将会成为节点加入该队列的尾部。

锁的可重入

当一个线程获得锁之后,再次进入同步快任然能获取到锁执行,实现原理是先判断当前线程是不是已经获取到锁的线程,如果是就把状态加1;释放锁的时候拿了几次锁就要释放几次

如果没有实现锁的可重入则会出现一个线程拿到了锁之后再次获取锁,这样就会自己等待自己出现死锁

公平锁和非公平锁

公平锁:一个线程去竞争锁的时候先看前面有没有其他线程在等待获取锁,如果有就不参与

非公平锁:一个线程去获取锁的时候不管前面有没有线程在等待获取锁都有参与竞争

ReentrantReadWriteLock的实现原理

读写锁的自定义同步器需要在同步状态(一个整型变量)上维护多个读线程和一个写线程的状态,使得该状态的设计成为读写锁实现的关键。如果在一个整型变量上维护多种状态,就一定需要“按位切割使用”这个变量,读写锁将变量切分成了两个部分,高16位表示读,低16位表示写。

读状态是所有线程获取读锁次数的总和,而每个线程各自获取读锁的次数只能选择保存在ThreadLocal中,由线程自身维护。

Condition的实现原理

condition是等待通知机制的另一种方式

等待队列是一个FIFO的队列,在队列中的每个节点都包含了一个线程引用,该线程就是在Condition对象上等待的线程,如果一个线程调用了Condition.await()方法,那么该线程将会释放锁、构造成节点加入等待队列并进入等待状态。

一个Condition包含一个等待队列,新增节点只需要将原有的尾节点nextWaiter指向它,并且更新尾节点即可。

调用Condition的signal()方法,将会唤醒在等待队列中等待时间最长的节点(首节点),在唤醒节点之前,会将节点移到同步队列中。

调用该方法的前置条件是当前线程必须获取了锁,可以看到signal()方法进行了isHeldExclusively()检查,也就是当前线程必须是获取了锁的线程。接着获取等待队列的首节点,将其移动到同步队列并使用LockSupport唤醒节点中的线程。

通过调用同步器的enq(Node node)方法,等待队列中的头节点线程安全地移动到同步队列。

Condition的signalAll()方法,相当于对等待队列中的每个节点均执行一次signal()方法,效果就是将等待队列中所有节点全部移动到同步队列中,并唤醒每个节点的线程。

来源:https://www.cnblogs.com/leeSmall/category/1143351.html

文章永久链接:https://tech.souyunku.com/?p=14362

赞(93) 打赏

版权归原创作者所有,任何形式转载请联系作者;搜云库技术团队 » 多线程笔记 四:AQS-AbstractQueuedSynchronizer
本站:免责声明!

评论 抢沙发

一个专注于Java技术系列文章的技术分享网站

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏