主要对之前的学习进行一个补充。
AbstractQueuedSynchronizer是实现同步容器的基础。在JUC中是一个非常主要的类,JDK文档是这么描述的:
为实现依赖于先进先出 (FIFO) 等待队列的阻塞锁和相关同步器(信号量、事件,等等)提供一个框架。此类的设计目标是成为依靠单个原子 int 值来表示状态的大多数同步器的一个有用基础。子类必须定义更改此状态的受保护方法,并定义哪种状态对于此对象意味着被获取或被释放。假定这些条件之后,此类中的其他方法就可以实现所有排队和阻塞机制。子类可以维护其他状态字段,但只是为了获得同步而只追踪使用 getState()、setState(int) 和 compareAndSetState(int, int) 方法来操作以原子方式更新的 int 值。
有这么几个重点:
1.FIFO;
2.依靠单个原子int值来表示状态;
3.子类必须定义更改此状态的受保护方法;
4.基于模板;
在如何使用还有这样一段描述:
为了将此类用作同步器的基础,需要适当地重新定义以下方法,这是通过使用 getState()、setState(int) 和/或 compareAndSetState(int, int) 方法来检查和/或修改同步状态来实现的:
- tryAcquire(int)
- tryRelease(int)
- tryAcquireShared(int)
- tryReleaseShared(int)
- isHeldExclusively()
默认情况下,每个方法都抛出 UnsupportedOperationException。这些方法的实现在内部必须是线程安全的,通常应该很短并且不被阻塞。定义这些方法是使用此类的 唯一受支持的方式。其他所有方法都被声明为 final,因为它们无法是各不相同的。
也就是说只有5个方法我们可以自己定义,其它的方法均为final。独占式使用tryAcquire(int)和tryRelease(int),共享式使用tryAcquireShared(int)和tryReleaseShared(int)。
API如下:
方法摘要 | |
| 以独占模式获取对象,忽略中断。 |
| 以独占模式获取对象,如果被中断则中止。 |
| 以共享模式获取对象,忽略中断。 |
| 以共享模式获取对象,如果被中断则中止。 |
| 如果当前状态值等于预期值,则以原子方式将同步状态设置为给定的更新值。 |
| 返回包含可能正以独占模式等待获取的线程 collection。 |
| 返回队列中第一个(等待时间最长的)线程,如果目前没有将任何线程加入队列,则返回 |
| 返回包含可能正在等待获取的线程 collection。 |
| 返回等待获取的线程数估计值。 |
| 返回包含可能正以共享模式等待获取的线程 collection。 |
| 返回同步状态的当前值。 |
| 返回一个 collection,其中包含可能正在等待与此同步器有关的给定条件的那些线程。 |
| 返回正在等待与此同步器有关的给定条件的线程数估计值。 |
| 查询是否其他线程也曾争着获取此同步器;也就是说,是否某个 acquire 方法已经阻塞。 |
| 查询是否有正在等待获取的任何线程。 |
| 查询是否有线程正在等待给定的、与此同步器相关的条件。 |
| 如果对于当前(正调用的)线程,同步是以独占方式进行的,则返回 |
| 如果给定线程的当前已加入队列,则返回 true。 |
| 查询给定的 ConditionObject 是否使用了此同步器作为其锁。 |
| 以独占模式释放对象。 |
| 以共享模式释放对象。 |
| 设置同步状态的值。 |
| 返回标识此同步器及其状态的字符串。 |
| 试图在独占模式下获取对象状态。 |
| 试图以独占模式获取对象,如果被中断则中止,如果到了给定超时时间,则会失败。 |
| 试图在共享模式下获取对象状态。 |
| 试图以共享模式获取对象,如果被中断则中止,如果到了给定超时时间,则会失败。 |
| 试图设置状态来反映独占模式下的一个释放。 |
| 试图设置状态来反映共享模式下的一个释放。 |
ReentrantLock就是基于AQS实现的,可以看看ReentrantLock的lokc()方法:
这个方法交给了同步器去处理,看看这个同步器:
继承了AQS,而且lock()方法还是abstact的。这是因为ReentrantLock有公平和非公平两种情况:
下面来看看NonfairSync中的lock()方法:
使用CAS设置当前状态,设置当前线程为独占线程。重点看这个acquire()方法。这个acquire()方法其实是AQS中的方法:
acquire()方法是这么描述的:
acquire
public final void acquire(int arg)
以独占模式获取对象,忽略中断。通过至少调用一次 tryAcquire(int) 来实现此方法,并在成功时返回。否则在成功之前,一直调用 tryAcquire(int) 将线程加入队列,线程可能重复被阻塞或不被阻塞。可以使用此方法来实现 Lock.lock() 方法。
参数:
arg
- acquire 参数。此值被传送给 tryAcquire(int),但它是不间断的,并且可以表示任何内容。
这是一个final方法,而在AQS中acquire()内部调用了tryAcquire()方法,上面也介绍过了,tryAcquire()方法需要被重写。看看实现的tryAcquire()方法:
反过来再看acquire()方法,如果获取不成功就要执行acquireQueued(addWaiter(Node.EXCLUSIVE),arg))方法。之前也介绍过,内部会维护一个FIFO,这个方法就是放进等待队列里面。看看AQS中的addWaiter(Node node)方法:
再看看acquireQueued()方法,刚刚直接将节点插入到了等待队列中,但是当前线程还没有被阻塞