通过维护一个共享资源状态( Volatile Int State )和一个先进先出( FIFO )的线程等待队列来实现一个多线程访问共享资源的同步框架。
AQS队列同步器(AbstractQueuedSynchronizer),是用来构建锁或者其他同步组件的基础框架。
它使用了一个int的成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。
同步器的主要使用方式是继承,子类通过基础同步器并实现它的抽象方法来管理同步状态,在抽象方法的实现过程中免不了对同步状态进行更改,这时需要使用同步器提供的三个方法(getState()、setState(int newState)和compareAndSetState(int expect,int update)来操作,因为他们能够保证状态的改变是安全的。
AQS内部维护的属性 volatile int state,state表示锁同步状态[独占锁表示是否占有,重入时自增。共享锁一般有其他count表示重入。
state的三种访问方式:getState() 直接从内存中读取同步器状态值
setState() 设置同步器状态值(一般是释放锁时使用)
compareAndSetState() 尝试修改锁状态(抢占锁时使用)
AQS定义两种队列
同步等待队列:用于维护获取锁失败的入队线程
条件等待队列:调用await()方法释放锁后,加入条件队列,等待条件唤醒再次争抢锁
AQS 为每个共享资源都设置一个共享资源锁,线程在需要访问共享资源时首先需要获取共享资源锁,如果获取到了共享资源锁,便可以在当前线程中使用该共享资源,如果获取不到,则将该线程放入线程等待队列,等待下一次资源调度。许多同步类的实现都依赖于AQS ,例如常用的 ReentrantLock、Semaphore、CountDownLatch。
AQS 维护了 volatile int state类型的变量,用于表示当前的同步状态。volatile虽然不能保证操作的原子性,但是能保证当前变量state的可见性。
state的访问方式有三种: getState()、setState()和 compareAndSetState(),均是原子操作,其中,compareAndSetState的实现依赖于 Unsafe的compareAndSwaplnt() 具体的。
同步队列
同步器依赖内部的同步队列(一个FIFO)双向队列完成状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成一个节点(Node)并将其加入同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次获取同步状态。
同步器包含了两个节点类型的引用,一个指向头节点,而另一个指向尾节点。
如果一个线程成功获取了同步状态,其他线程无法获取到同步状态,转而被构造成节点并加入同步队列中,而加入队列的过程必须要保证线程安全,因此同步器提供了一个基于CAS设置尾节点的方法:compareAndSetTail(Node expect,Node update),它需要传递当前线程认为的尾节点和当前节点,只有设置成功后,当前节点才正式与之前的尾节点建立关联。
同步队列遵循FIFO,首节点是获取同步状态成功的节点,首节点的线程在释放同步状态时,将会唤醒后继节点,而后继节点将会在获取同步状态将会在获取同步状态成功时将自己设置为首节点。
因为设首节点是通过获取同步状态完成的线程来完成的,由于只有一个线程能够获取到同步状态,因此设置头节点的方法并不需要CAS来保证。只需要将首节点设置为原首节点的后继节点并断开原首节点的next引用即可。
AQS 定义了两种资源共享方式 :独占式 (Exclusive)和共享式(Share)
独占式:只有一个线程能执行,具体的 Java 实现有 ReentrantLock。
共享式:多个线程可同时执行,具体的 Java 实现有 Semaphore和CountDownLatch。
CountDownLatch,可以实现某一些线程,等待其他线程执行完之后,才可以执行
CyclicBarrier,实现了多个线程间的相互等待,直到大家等待的条件满足了,就可以一起执行了
Semaphore,是信号量,可以控制并发的线程数
CountdownLatch
用来进行线程同步协作,它允许一个或多个线程一直等待,直到其他线程的操作执行完毕再执行。
其中构造参数用来初始化等待计数值,await()用来等待计数归零,countDown()用来让计数减一.
CountDownLatch 是共享锁的一种实现,它默认构造 AQS 的 state 值为 count。当线程使用 countDown方法时,其实使用了 tryReleaseShared 方法以CAS 的操作来减少 state ,直至 state 为 0 就代表所有的线程都调用了countDown方法。当调用 await 方法的时候,如果 state 不为0,就代表仍然有线程没有调用 countDown 方法,那么就把已经调用过 await 的线程都放入阻塞队列 Park ,并自旋 CAS 判断 state == 0,直至最后一个线程调用了 countDown ,使得 state == 0,于是阻塞的线程便判断成功,全部往下执行。
Semaphore
信号量,用来限制能同时访问共享资源的线程上限。acquire()获取许可,release()释放许可