AQS(抽象同步队列)
1.juc包中各种锁都是基于其实现的
AQS是一个FIFO的双向链表,其内部有一个int类型的参数state 可以通过 getState setState compareAndSetState 函数修改其值 对于 ReentrantLock 现来说 state 可以用 来表示前线程获取锁的可重入次数 ;对于 读写锁 ReentrantReadWriteLock 来说 state 16位表示读状态,也就是获取该读锁的次数,低 16 位表示获取到写锁的线程的可重入次数;对于 semaphore 来说,state 用来表示当前可用信号的个数;对于 CountDownlatch 来说,state 来表示计数器当前的值。
2.AQS的独占锁和共享锁
对于AQS 来说,线程同步的关键是对状态值 state 进行操作 根据 state 是否属于一个线程 ,操作 state 方式分为独占方式和共享方式 在独占方式下获取和释放资源使用的
方法为 void acquire( int arg) void acquirelnterruptibly(int arg) boolean release( int arg)
在共享方式下获取和释放资源的方法为: void acquireShared(int arg) void
acqt eSharedinterruptibly(int arg) boolean easeShared(int arg)
3.独占锁(排它锁)的获取和释放锁:
独占锁:
获取锁 acquiref方法):锁与具体某个线程绑定,当某个线程获取到锁时其他线程再获取该锁时会被阻塞。
如reentrantLock,AQS内部会使用CAS先把state从0变为1,并把当前锁的持有者设置为该线程,当该线程再次获取锁时state由1变为2,即设置可重入次数。其他线程尝试获取锁时,state不为0且该锁持有者不是当前线程则被放入阻塞队列队尾后调用Locksupport.park(this)将该线程挂起。
释放锁 release方法):先CAS把state从减一变为0,当变为0时,将持有锁的线程设置为null。同时调用LockSupport.unpark(thread) 将阻塞队列里的线程唤醒。
共享锁:
ReentrantReadWriteLock 里的读锁是共享锁,写锁是独占锁。对于读锁获取和释放锁就是使用 CAS把state的高16位进行增减。
protected final int tryAcquireShared(int unused) {
/*
*
*/
// (1)获取当前状态值
Thread current = Thread.currentThread();
int c = getState();
//(2)判断是否写锁被占用
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
//(3)获取读锁计数
int r = sharedCount(c);
// (4)尝试获取锁 多个读线程只有 1个会成功,不成功的进入fullTryAcquireShared进行重试
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
// (5 )第1个线程获取读锁
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
//(6)如采当前线程是第1个获取读锁的线程
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
// (7)记录最后1个获取读锁的线程或记录其他线程读锁的可重入数
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
// 类似tryAcquireShared ,但是自旋获取
return fullTryAcquireShared(current);
}
4.AQS的入队尾操作:
1.先初始化队列的哨兵节点,头尾都指向哨兵节点,然后将要插入的node节点cas设置为尾部节点,node节点前驱节点设置为原来的尾部节点,尾部节点的后驱节点设置为该node节点:
``
private Node addWaiter(Node mode) {
Node node = new Node(mode);
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
//设置前置节点为原来的尾节点
node.setPrevRelaxed(oldTail);
if (compareAndSetTail(oldTail, node)) {
//设置原来的尾部节点的后置节点为该node节点
oldTail.next = node;
return node;
}
} else {
//初始化队列哨兵节点,头和尾指向哨兵节点
initializeSyncQueue();
}
}
Reentrantlock的公平锁和非公平锁(默认)
下面是非公平锁源码:这里线程在获取锁前并没有查看当前AQS队列里面是否有比自己更早请求该锁的线程 而是使用了抢夺策略
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//这里线程在获取锁前并没有查看当前AQS队列里面是否有比自己更早请求该锁的线程 而是使用了抢夺策略
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;
}
下面是公平锁源码:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//以下是公平锁的实现策略:如果当前队列为空或者当前线程的节点是队列的第一个节点
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
//公平锁的实现方法,这个方法表示是否有前驱节点
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
/**
h!=t 表示当前队列不为空 ;
如果 h!=t 并且 ==null 则说明有一个元素将要作为 QS 第一个节点入队列;
如果 h!=t 并且 s!=null && s.thread != Thread.cunentThread()则说明队列 面的第1个元素不是当前线程,
*/
}