目录
(1)ReentranLock与AQS的关系
(2)动态调试ReentranLock程序的加锁调用过程
(3)AQS的类结构
(4)AQS一些子类实现的应用介绍
AQS是AbstractQueuedSynchronizer的缩写,它提供一个框架,用于实现依赖于先进先出(FIFO)等待队列的阻塞锁和相关的同步器(信号灯,事件等)。 此类旨在为大多数依赖单个原子{@code int}值表示状态的同步器提供有用的基础。 子类必须定义更改此状态的受保护方法,并定义该状态对于获取或释放此对象而言意味着什么。 鉴于这些,此类中的其他方法将执行所有排队和阻塞机制。 子类可以维护其他状态字段,但仅跟踪使用方法{@link #getState},{@ link #setState}和{@link #compareAndSetState}进行原子更新的{@code int}值的同步性。
AQS有很多具体的实现子类,比如ReentranLock、CountDownLatch、CyclicBarrier、Semaphore等核心都依赖AQS类实现。
(1)ReentranLock与AQS的关系
拿ReentranLock来说,其主要代码逻辑是依靠其内部抽象类Sync具体实现。Sync是AbstractQueuedSynchronizer的子类。
abstract static class Sync extends AbstractQueuedSynchronizer
而Sync抽象类又有两个实现子类FairSync和NonfairSync。
ReentrantLock与Sync-NonfairSync-NonfairSync的关系
Sync-NonfairSync-NonfairSync与AQS的关系
(2)动态调试ReentranLock程序的加锁调用过程
之前我们用ReentranLock实现了一个固定容量的容器,实现生产者和消费者的模型。
https://blog.csdn.net/phs999/article/details/109107758
package interview.question2;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 使用Condition的好处在于可以把生产线程和消费线程区分开进行唤醒
* @param <T>
*/
public class Container2<T> {
final private LinkedList<T> lists = new LinkedList<>();
final private int MAX = 10;
ReentrantLock lock = new ReentrantLock();
Condition consumer = lock.newCondition();
Condition producer = lock.newCondition();
public void put(T o) {
try {
lock.lock();
while (lists.size() == MAX) {
System.out.println("生产完毕,生产者 "+Thread.currentThread().getName()+" 阻塞中");
producer.await();
}
lists.add(o);
consumer.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public T get() {
T t=null;
lock.lock();
try {
while (lists.size() == 0) {
System.out.println("库存为0,消费者 "+Thread.currentThread().getName()+" 阻塞中");
consumer.await();
}
t = lists.removeFirst();
producer.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return t;
}
public synchronized int getCount() {
return lists.size();
}
public static void main(String[] args) throws InterruptedException {
Container2 c = new Container2();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 5; j++) {//每个消费者线程消耗5个对象
System.out.println(Thread.currentThread().getName()+" 消费了 "+c.get()+" 库存剩余:"+c.getCount());
}
}, "consumer" + i).start();
}
TimeUnit.SECONDS.sleep(5);
for (int i = 0; i < 2; i++) {
new Thread(() -> {
for (int j = 0; j < 25; j++) {//每个生产者线程生产25个对象
c.put(Thread.currentThread().getName() + " " + j);
System.out.println(Thread.currentThread().getName()+" 生产了 "+Thread.currentThread().getName()+" "+j+" 库存剩余:"+c.getCount());
}
}, "producer" + i).start();
}
}
}
我们对lock()方法加断点,调用过程如下:




如果compareAndSetState()方法将state状态值成功修改返回true,则执行setExclusiveOwnerThread()方法设置独占进行,当前线程获得独占锁。

如果compareAndSetState()方法没有将state状态值成功修改,返回false,则说明目前有线程已获得这个锁。



尝试获取锁,如果getState()返回的值是0,则可以继续使用CAS的方式修改state的值,如果不是0的话,则判断当前的获得锁的独占 线程是不是现在尝试获取的的这个线程,如果是,则是锁重入,只需要给state+1就行了。如果以上都不是,则尝试获取锁的线程进入等待队列。

未获得锁的线程加入等待线程队列。

使用CAS的方式将该线程已Node对象的方式加入到等待队列尾部。


(3)AQS的类结构

AQS类中state变量,对于ReentranLock来说,用于标识是否有线程获得锁,没有线程获得锁则该值为0.
对于CountDownLatch来说,则代表countdown()方法调用的次数,调用多少次后state为0时则线程唤醒。
该变量使用volatile修饰,保证线程可见性。同时该类中还有一个Node对象组成的双向列表。

AQS的核心就是cas+volatile的应用,volatile保证了state变量的线程可见性,cas用于线程修改state值以及将等待线程添加到等待队列(双向链表中)。
(4)AQS一些子类实现的应用介绍
ReentranLock、CountDownLatch、CyclicBarrier、Semaphore |