4.ReentrantLock自己阅读源码之后的感受

自己总结:
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);
    }
}

你可能感兴趣的:(4.ReentrantLock自己阅读源码之后的感受)