自己总结:
ReentrantLock在new的时候,传入的是true或者false,生成不同的公平或者非公平的对象,叫sync,sync会调用lock函数,进行加锁;这个lock会根据自身的同步器调用自身的lock方法;
对于非同步的lock方法,上来首先进行同步(cas),如果,如果同步成功,就将锁的owner设置为此线程,进行排他,代表此锁被抢;此方法为冗余的,就是为了加快获取锁的速度;
如果同步不成功,调用acquire方法,然后会调用AQS的acquire方法:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
首先调用tryAcquire方法,如果调用成功,代表获取锁成功,如果获取失败,需要将次线程加入队列;
tryAcquire方法在公平或者非公平锁中实现,AQS没实现,调用会抛出异常;如果是非公平的锁,会
nonfairTryAcquire,获取当前线程,然后获取当前的状态state,如果是0表示没有人抢占锁,可以获取,然后CAS获取锁,如果获成功,则将owner设置成当前线程,然后返回true,如果失败,返回false则会将当前线程加入队列中。还有一种情况是,c不等于0,那就是重入了,如果当前线程和排他线程相同,则可以重入啊,然后将state设置为acquires+c,返回true;
当获取锁失败之后,就会将当前线程加入队尾addWaiter→里面有一个冗余的操作,就是队尾不等于null,就直接往队尾通过cas加入节点,如果为null,则用enq()函数加入,为null可能需要让头尾相等,不为null,再执行以下冗余操作。
然后执行acquireQueue,见名知意,就是从队列里获取锁嘛,首先一个for死循环,只有一个出口,就是获取了锁,然后返回;如何获取锁呢,判断当前节点的前继节点是否为头节点,因为只有头节点才有可能获取锁,然后获取锁成功,则把当前节点设置为头结点,然后返回;
如果获取当前节点不会head节点,或者获取锁失败了,这时候,就需要挂起了;
在挂起之前,首先要扫描本节点之前的节点,是否为取消状态(cancel),如果是取消状态,则向前继续寻找;然后挂起当前线程,用LockSupport.park(this);
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}