AQS是多线程同步器,它是J.U.C包中多个组件的底层实现,如Lock、CountDownLatch、Semaphore等都用到了AQS.

 

从本质上来说,AQS提供了两种锁机制,分别是排它锁,和 共享锁。

排它锁,就是存在多线程竞争同一共享资源时,同一时刻只允许一个线程访问该共享资源,也就是多个线程中只能有一个线程获得锁资源,比如Lock中的ReentrantLock重入锁实现就是用到了AQS中的排它锁功能。

共享锁也称为读锁,就是在同一时刻允许多个线程同时获得锁资源,比如CountDownLatch和Semaphore都是用到了AQS中的共享锁功能。

 

AQS作为互斥锁而言,它在整体的设计中,需要解决三个核心问题

  1. 互斥变量的设计以及如何保证多线程同时更新互斥变量时的线程安全性
  2. 未竞争到锁资源的线程的等待以及竞争到锁的资源释放锁之后的唤醒
  3. 锁竞争的公平性和非公平性

AQS采用了一个int类型的互斥变量state来记录锁竞争的状态,0表示当前没有任何线程竞争锁资源,大于等于1表示有线程正在持有锁资源。

一个线程来获取锁资源时,首先判断state是否等于0,也就是无锁状态。如果是,则更新成1,表示占用锁。而这个过程中如果多个线程同时做这样的一个操作,就会导致线程安全问题,因此AQS采用了CAS机制来保证state互斥变量更新的原子性。

未获得锁的线程,通过Unsafe类中的park方法进行阻塞,把阻塞的线程按照先进先出的原则加入到一个双向链表结构中存储。

当获取到锁资源的线程释放锁之后,会从这个双向链表的头部唤醒下一个等待中的线程再去竞争锁。

最后,关于锁竞争的公平性和非公平性问题,AQS的处理方式是,在竞争锁资源时。

公平锁需要判断双向链表中否有阻塞的线程,如果有,则需要排队等待。

非公平锁的处理方式是,不管双向链表中是否存在等待竞争锁的线程,它都会直接去尝试更改互斥变量state竞争锁,假设在临界点时,获得锁的线程释放锁,而当前抢占锁的线程正好能够拿到锁。