一、什么是AQS
AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Semaphore,其他的诸如ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的。当然,我们自己也能利用AQS非常轻松容易地构造出符合我们自己需求的同步器。
二、AQS 的核心思想
AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。 CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。 AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。
三、AQS的工作原理
一个线程来获取锁资源的时候,首先会判断state是否等于0,也就是说它是无锁状态。如果是,则把这个state更新成1,表示占用到锁。
而这个过程中,如果存在同时多次做这样的一个操作,就会导致线程安全性问题。因此AQS采用了CAS机制,去保证state互斥变量更新的一个原子性。未获得到锁的线程通过Unsafe类中的park方法去进行阻塞,把阻塞的线程按照先进先出的原则,去加入到双向链表的一个结构中。
当获得锁资源的线程释放锁之后,会从这样一个双向链表的头部去唤醒下一个等待的线程,再去竞争锁。
四、总结
AQS,全称为AbstractQueuedSynchronizer,是Java并发编程中的一个重要组件。它是一个用于构建同步器的框架,提供了一套基于状态、队列和锁的机制,用于实现线程间的同步和协调。下面我将从几个方面谈谈对AQS的理解:
- AQS的核心思想:AQS的核心思想是使用一个int类型的状态变量来表示同步状态,通过CAS(Compare-And-Swap)操作来保证状态变量的原子性更新。同时,AQS使用一个FIFO队列来管理阻塞的线程,当同步状态满足时,唤醒队列中的线程来竞争锁。
- AQS提供的两种锁机制:AQS提供了排他锁和共享锁两种锁机制。排他锁同一时间只允许一个线程访问共享资源,如ReentrantLock;共享锁则允许多个线程同时访问共享资源,如Semaphore和CountDownLatch。这两种锁机制都是通过状态变量和队列的管理来实现的。
- AQS的工作流程:当线程尝试获取锁时,首先会检查同步状态是否满足,如果满足则直接获取锁,否则会将当前线程加入等待队列并阻塞。当同步状态满足或队列中的线程被唤醒时,线程会尝试获取锁,成功则执行同步代码,否则继续阻塞。同步代码执行完毕后,会释放锁并唤醒等待队列中的下一个线程。
- AQS的应用:AQS作为Java并发包(java.util.concurrent)的底层实现,被广泛应用于各种同步器的实现,如ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch等。这些同步器都是基于AQS实现的,通过组合和加工AQS提供的方法来实现各自的同步需求。
总的来说,AQS提供了一种基于状态和队列的同步机制,通过原子性的状态变量和FIFO队列来管理线程的同步和协调。它是Java并发编程中的重要组件,为各种同步器的实现提供了统一的框架和支持。