AQS(线程同步器)介绍

1. 什么是AQS

AQS - AbstractQueuedSynchronizer

AQS定义了实现线程同步器的基础框架, 线程同步器的作用是协调多个线程对共享资源的访问。

AbstractQueuedSynchronizer派生出Java并发包的重要类, 如下:

- CountDownLatch(倒计时同步器)

- CyclicBarrier(循环栅栏同步器)

- Semaphore(信号量同步器) [ˈseməfɔ:(r)]

- ReentrantLock(可重入锁)
在多个线程共享同一个资源时, 可以基于AQS的相关实现类来实现多线程对该资源的同步访问, 
这样可以避免多线程并发访问导致的数据不一致问题。


2. AQS基础
由于AQS里面用到了大量的CAS, 因此学习AQS之前我们还是有必要简单的先了解下CAS、公平锁和非公平锁。
 CAS
CAS 全称是 compare and swap,是一种用于在多线程环境下实现同步功能的机制。

CAS 操作包含三个操作数: 内存位置、预期数值和新值。

CAS 的实现逻辑是将内存位置处的数值与预期数值相比较
    - 若相等   --> 则将内存位置处的值替换为新值。
    - 若不相等 --> 则不做任何操作,这个操作是个原子性操作, java里面的AtomicInteger等类都是通过cas来实现的。
公平锁(先到先得)
先到先得:
    多个线程按照申请锁的顺序去获得锁, 线程会直接进入队列去排队, 队列中第一个才能获得到锁。

优点:
    等待锁的线程不会饿死, 每个线程都可以获取到锁。
缺点:
    整体吞吐效率相对非公平锁要低
    等待队列中除第一个线程以外的所有线程都会阻塞
    CPU唤醒阻塞线程的开销比非公平锁大

 

非公平锁(弱肉强食)

多个线程去获取锁的时候, 会直接去尝试获取,
获取不到, 再去进入等待队列,
如果能获取到,就直接获取到锁。

优点:
    可以减少CPU唤醒线程的开销
    整体的吞吐效率会高点
    CPU也不必唤醒所有线程, 减少了唤起线程的数量
缺点:
    处于等待队列中的线程有可能要等很久才会获得锁, 可能会饿死。
    
举个例子比喻一下:
    比如我们去食堂就餐的时候都要排队, 大家都按照先来后到的顺序排队打饭,这就是公平锁。
    
    如果等到你准备拿盘子打饭的时候, 直接蹦出了一个五大三粗的胖子插队到你前面,
    你看打不赢他只能忍气吞声让他插队,等胖子打完饭了又来个小个子也来插你队,
    这时候你没法忍了, 直接大吼一声让他滚, 这个小个子只能屁颠屁颠到队尾去排队了这就是非公平锁。

 

3. AQS源码分析

AQS的基本属性:

 

// 头节点
private transient volatile Node head;

// 阻塞的尾节点,每个新的节点进来,都插入到最后,也就形成了一个链表
private transient volatile Node tail;

// 这个是最重要的,代表当前锁的状态,0代表没有被占用,大于 0 代表有线程持有当前锁
// 这个值可以大于 1,是因为锁可以重入,每次重入都加上 1
private volatile int state;

// 代表当前持有独占锁的线程,举个最重要的使用例子,因为锁可以重入
// reentrantLock.lock()可以嵌套调用多次,所以每次用这个来判断当前线程是否已经拥有了锁
// if (currentThread == getExclusiveOwnerThread()) {state++}
private transient Thread exclusiveOwnerThread; //继承自AbstractOwnableSynchronizer

 

 

 

 

 
 

 


posted @ 2021-03-11 21:20  Master HaKu  阅读(629)  评论(0编辑  收藏  举报