AQS的设计核心原理:
1.自旋,也就是死循环
2.cas:比较并且交换,
3.LockSupport.lock 阻塞
4.入队列
4大核心思想:假设一个线程进来要抢锁,一直进行死循环的话,那么如果抢到锁的线程进行的业务时间很长,就会一直耗费cpu的时间片在进行死循环抢锁,这是我们就会使用cas来进行抢锁,如果成功地话,就会记录一个抢锁成功地状态,如果抢锁失败就会进行阻塞,同时把阻塞的线程记录在队列中,当抢到锁的线程执行玩业务释放锁, 那么在队列中的阻塞线程就会进行抢线程操作,这里有个问题,是否抢锁的线程是公平的,也就是锁,如果有很长队列那么刚进来的线程到底是插队抢锁还是排队抢锁呢?这就涉及到公平锁和非公平锁。公平锁也就是会从队列中取队头的线程。非公平锁直接去抢cpu时间片,抢到的话就立马执行。这既是AQS的核心思想。
下面我们来看看具体的代码实现:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
判断当前线程是否获取到锁,如果获取到锁,同时判断队列中是否还有其他线程,如果没有其他线程那么进行AQS,同时设置当前线程为排他线程,下面的else if是判断当前线程是否是二次获取到锁,这也从侧面验证了ReentrantLock是可重入锁,如果是的话,那么锁状态自加1.
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;
}
}
如果没有抢到锁那么直接进入队列,具体代码如下:
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
入队列也就是通过双向链表,CHL三个大神写的。当第一个线程进来抢锁,此时双向链表的队列头为空,尾也是空,那么进入enq逻辑。
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
这里有个死循环,也就是自旋的目的就是保证必须入队列必须成功,否则就会导致线程丢失。线面那个cas的作用是为了保证入队列的原子性,比如双向链表的指针一个操作好了头结点,然后尾结点插入的时候被打断了,这样就会导致各种问题,为了保证成功入队列就需要用cas,没拿到锁的线程一定不能被丢弃,
看下这行代码:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
这里设计到线程的几个状态:
signal=-1 代表可被唤醒
cancelled=1 代表出现异常,由中断引起的。,也就是说第一轮循环的时候会修改head的状态,将signal=-1标记可被唤醒的状态,第二轮在进行阻塞线程,但在节点阻塞前还得尝试获取一次锁,能够获取到锁的线程则出队列,并且把head往后挪动一个节点,新节点就是当前节点。不能获取到锁则阻塞等待被唤醒。
释放锁的逻辑代码:
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
线程当前的状态-1,如果该状态为0,那么代表可以释放,同时把变量exclusive置为null,state=0