相关博客:
https://blog.csdn.net/Dongguabai/article/details/82691626
https://blog.csdn.net/Dongguabai/article/details/82461675
主要对之前的学习进行一个补充。
AbstractQueuedSynchronizer是实现同步容器的基础。在JUC中是一个非常主要的类,JDK文档是这么描述的:
为实现依赖于先进先出 (FIFO) 等待队列的阻塞锁和相关同步器(信号量、事件,等等)提供一个框架。此类的设计目标是成为依靠单个原子 int 值来表示状态的大多数同步器的一个有用基础。子类必须定义更改此状态的受保护方法,并定义哪种状态对于此对象意味着被获取或被释放。假定这些条件之后,此类中的其他方法就可以实现所有排队和阻塞机制。子类可以维护其他状态字段,但只是为了获得同步而只追踪使用
getState()
、setState(int)
和compareAndSetState(int, int)
方法来操作以原子方式更新的 int 值。
有这么几个重点:
1.FIFO;
2.依靠单个原子int值来表示状态;
3.子类必须定义更改此状态的受保护方法;
4.基于模板;
在如何使用还有这样一段描述:
为了将此类用作同步器的基础,需要适当地重新定义以下方法,这是通过使用
getState()
、setState(int)
和/或compareAndSetState(int, int)
方法来检查和/或修改同步状态来实现的:默认情况下,每个方法都抛出
UnsupportedOperationException
。这些方法的实现在内部必须是线程安全的,通常应该很短并且不被阻塞。定义这些方法是使用此类的 唯一受支持的方式。其他所有方法都被声明为 final,因为它们无法是各不相同的。
也就是说只有5个方法我们可以自己定义,其它的方法均为final。独占式使用tryAcquire(int)和tryRelease(int),共享式使用tryAcquireShared(int)和tryReleaseShared(int)。
API如下:
方法摘要 | |
---|---|
void |
acquire(int arg) 以独占模式获取对象,忽略中断。 |
void |
acquireInterruptibly(int arg) 以独占模式获取对象,如果被中断则中止。 |
void |
acquireShared(int arg) 以共享模式获取对象,忽略中断。 |
void |
acquireSharedInterruptibly(int arg) 以共享模式获取对象,如果被中断则中止。 |
protected boolean |
compareAndSetState(int expect, int update) 如果当前状态值等于预期值,则以原子方式将同步状态设置为给定的更新值。 |
Collection<Thread> |
getExclusiveQueuedThreads() 返回包含可能正以独占模式等待获取的线程 collection。 |
Thread |
getFirstQueuedThread() 返回队列中第一个(等待时间最长的)线程,如果目前没有将任何线程加入队列,则返回 null . 在此实现中,该操作是以固定时间返回的,但是,如果其他线程目前正在并发修改该队列,则可能出现循环争用。 |
Collection<Thread> |
getQueuedThreads() 返回包含可能正在等待获取的线程 collection。 |
int |
getQueueLength() 返回等待获取的线程数估计值。 |
Collection<Thread> |
getSharedQueuedThreads() 返回包含可能正以共享模式等待获取的线程 collection。 |
protected int |
getState() 返回同步状态的当前值。 |
Collection<Thread> |
getWaitingThreads(AbstractQueuedSynchronizer.ConditionObject condition) 返回一个 collection,其中包含可能正在等待与此同步器有关的给定条件的那些线程。 |
int |
getWaitQueueLength(AbstractQueuedSynchronizer.ConditionObject condition) 返回正在等待与此同步器有关的给定条件的线程数估计值。 |
boolean |
hasContended() 查询是否其他线程也曾争着获取此同步器;也就是说,是否某个 acquire 方法已经阻塞。 |
boolean |
hasQueuedThreads() 查询是否有正在等待获取的任何线程。 |
boolean |
hasWaiters(AbstractQueuedSynchronizer.ConditionObject condition) 查询是否有线程正在等待给定的、与此同步器相关的条件。 |
protected boolean |
isHeldExclusively() 如果对于当前(正调用的)线程,同步是以独占方式进行的,则返回 true 。 |
boolean |
isQueued(Thread thread) 如果给定线程的当前已加入队列,则返回 true。 |
boolean |
owns(AbstractQueuedSynchronizer.ConditionObject condition) 查询给定的 ConditionObject 是否使用了此同步器作为其锁。 |
boolean |
release(int arg) 以独占模式释放对象。 |
boolean |
releaseShared(int arg) 以共享模式释放对象。 |
protected void |
setState(int newState) 设置同步状态的值。 |
String |
toString() 返回标识此同步器及其状态的字符串。 |
protected boolean |
tryAcquire(int arg) 试图在独占模式下获取对象状态。 |
boolean |
tryAcquireNanos(int arg, long nanosTimeout) 试图以独占模式获取对象,如果被中断则中止,如果到了给定超时时间,则会失败。 |
protected int |
tryAcquireShared(int arg) 试图在共享模式下获取对象状态。 |
boolean |
tryAcquireSharedNanos(int arg, long nanosTimeout) 试图以共享模式获取对象,如果被中断则中止,如果到了给定超时时间,则会失败。 |
protected boolean |
tryRelease(int arg) 试图设置状态来反映独占模式下的一个释放。 |
protected boolean |
tryReleaseShared(int arg) 试图设置状态来反映共享模式下的一个释放。 |
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()方法:
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取状态
int c = getState();
if (c == 0) {
//判断当前线程是否获取到锁
if (compareAndSetState(0, acquires)) {
//设置为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
//如果是当前线程开始判断重入
else if (current == getExclusiveOwnerThread()) {
//状态加
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//更新状态
setState(nextc);
//获取成功
return true;
}
return false;
}
反过来再看acquire()方法,如果获取不成功就要执行acquireQueued(addWaiter(Node.EXCLUSIVE),arg))方法。之前也介绍过,内部会维护一个FIFO,这个方法就是放进等待队列里面。看看AQS中的addWaiter(Node node)方法:
再看看acquireQueued()方法,刚刚直接将节点插入到了等待队列中,但是当前线程还没有被阻塞(具体可参看:https://blog.csdn.net/Dongguabai/article/details/82461675)。