当前位置: 首页>大数据>正文

Android JUC05 --- AQS

一.什么是AQS?

1.AQS,全程AbstractQueuedSynchronizer,位于java.util.concurrent.locks包下.

2.是JDK1.5提供的一套用于实现阻塞锁和一系列依赖FIFO等待队列的同步器(First Input First Output先进先出)的框架实现。是除了java自带的synchronized 关键字之外的锁机制。 可以将AQS作为一个队列来理解

3.我们常用的ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier等并发类均是基于AQS来实现的。具体用法是通过继承AQS,并实现其模板方法,来达到同步状态的管理。

总结:

1.JVM官方给我们了一套synchronize锁机制的实现,偏向-》轻量-》重量-->Monitor(C++实现)。

2.JAVA语言层面给我们另外写了一套锁机制(同步器)(Java实现)。

那么写这东西区别于synchronize有什么好处?

二.AQS核心思想

AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

Android JUC05 --- AQS,第1张
AQS核心思想

lock--->当前线程---》state--->1----->其他线程park()--->其他线程一直自旋---》改STATE变量---》0,1

注意:sychronized 每次都要访问对象头,而AQS是访问原子变量!!!

三.AQS使用方式

AQS设计是基于模板方法模式的,一般的使用方式是:

1.使用者继承AbstractQueuedSynchronizer并重写指定的方法。(这些重写方法很简单,无非是对于共享资源state的获取和释放)

2.将AQS组合在自定义同步组件的实现中,并调用其模板方法,而这些模板方法会调用使用者重写的方法。

四.AQS的模板方法

Android JUC05 --- AQS,第2张
模板方法

五.自定义,使用总结

Android JUC05 --- AQS,第3张
使用1
Android JUC05 --- AQS,第4张
使用2
Android JUC05 --- AQS,第5张
使用3

AQS是啥玩意?

doglee 帮我们搭出了一个基础的锁实现的架子

ReentrantLock是doglee帮我们实现的锁。

六.ReentrantLock原理分析

1.怎么加锁的?

Android JUC05 --- AQS,第6张
竞争成功
Android JUC05 --- AQS,第7张
竞争不成功

2.怎么解锁的?

3.阻塞队列是个啥玩意?

非公平锁阻塞队列自旋过程:不断循环改变位置,非公平竞争!!!

Android JUC05 --- AQS,第8张
ReentrantLock加锁流程

每个条件变量其实就对应着一个等待队列,其实现类是 ConditionObject

await 流程

开始 Thread-0 持有锁,调用 await,进入 ConditionObject 的 addConditionWaiter 流程

创建新的 Node 状态为 -2(Node.CONDITION),关联 Thread-0,加入等待队列尾部

Android JUC05 --- AQS,第9张
await 流程
Android JUC05 --- AQS,第10张
aqs加锁流程图

七.同步方案对比

1.wait/notify:依托于synchronized,基于VM底层对于阻塞的实现,使用waitSet作为等待机制,set结构的问题,要么是随机一个(set的提取算法),要么是全部提出来唤醒。

2.await/signal:依赖于ReentrantLock条件变量,已经用条件变量与AQS体系作为唤醒机制,本质上底层是park/unpark实现阻塞。

3.park/unpark:以thread为操作对象,操作更精准,可以准确地唤醒某一个线程(notify随机唤醒一个线程,notifyAll唤醒所有等待的线程),增加了灵活性。

其实park/unpark的设计原理核心是“许可”:park是等待一个许可,unpark是为某线程提供一个许可

但是这个“许可”是不能叠加的,“许可”是一次性的。

比如线程B连续调用了三次unpark函数,当线程A调用park函数就使用掉这个“许可”,如果线程A再次调用park,则进入等待状态。

八.ReentrantReadWriteLock读写锁

1.读写锁指一个资源能够被多个读线程访问,或者被一个写线程访问,但是不能同时存在读写线程。

2.ReentrantReadWriteLock中有两个静态内部类:ReadLock读锁和WriteLock写锁,这两个锁实现了Lock接口ReentrantReadWriteLock支持可重入,同步功能依赖自定义同步器(AbstractQueuedSynchronizer)实现,读写状态就是其同步器的同步状态。

写锁的获取和释放:

写锁WriteLock是支持重进入的排他锁。如果当前线程已经获取了写锁,则增加写状态。如果当前线程在获取读锁时,读锁已经被获取或者该线程不是已获取写锁的线程,则当前线程进入等待状态。读写锁确保写锁的操作对读锁可见。写锁释放每次减少写状态,当前写状态为0时表示写锁已背释放。

读锁的获取与释放:

读锁ReadLock是支持重进入的共享锁(共享锁为shared节点,对于shared节点会进行一连串的唤醒,知道遇到一个读节点),它能够被多个线程同时获取,在没有其他写线程访问(写状态为0)时,读锁总是能够被成功地获取,而所做的也只是增加读状态(线程安全)。如果当前线程已经获取了读锁,则增加读状态。如果当前线程在获取读锁时,写锁已经被获取,则进入等待状态。

代码:

Android JUC05 --- AQS,第11张
ReentrantReadWriteLock
Android JUC05 --- AQS,第12张
同时读
Android JUC05 --- AQS,第13张
输出结果
Android JUC05 --- AQS,第14张
读+写
Android JUC05 --- AQS,第15张
输出结果

九.CyclicBarrier和CountDownLatch

CountDownLatch :一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;(具体业务场景:我们在玩LOL英雄联盟时会出现十个人不同加载状态,但是最后一个人由于各种原因始终加载不了100%,于是游戏系统自动等待所有玩家的状态都准备好,才展现游戏画面)。

CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再继续一起执行。而且可以重用。

CountDownLatch是计数器,线程完成一个记录一个,只不过计数不是递增而是递减,而CyclicBarrier更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行。

AQS依托业务来开发并发工具

底层原理应用的是CAS原子变量+队列+park&unpark的支撑来添加业务去具体实现锁的定义。

Android JUC05 --- AQS,第16张
CountDownLatch

输出结果:

Android JUC05 --- AQS,第17张
输出结果
Android JUC05 --- AQS,第18张
CyclicBarrier

十.并发编程的总结

1.内存模型(最根本的是吸收JAVA对于线程处理,在内存数据角度的本质是什么)

2.JVM锁机制(JAVA常规情况下对于同步处理的手段是怎么做的)

3.原子变量-CAS(无锁并发)(常规手段的优化--》CAS理论来支撑对于无锁并发的处理)

4.线程池(对于线程的优化在于如何合理地去利用CPU核数的优势)

5.AQS(如何正对于专门的业务,利用CAS与PARK机制,完成对于特殊业务定制化同步诉求)

?


https://www.xamrdz.com/bigdata/7wn1994320.html

相关文章: