欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

多线程学习--关于ReentrantLock源码详细解读

程序员文章站 2022-03-10 09:05:36
对于源码的阅读,有几点总结:1.跑不通的源码,不读;2.解决问题为目的;3.总体把握,先不扣细节;4.抓住一条线,往下阅读话不多说,简单demo断点调试,JDK版本为1.8public class TestReentrantLock { private static volatile int i = 0; public static void main(String[] args) { ReentrantLock lock = new Reent...

对于源码的阅读,有几点总结:

1.跑不通的源码,不读;

2.解决问题为目的;

3.总体把握,先不扣细节;

4.抓住一条线,往下阅读

话不多说,简单demo断点调试,JDK版本为1.8

public class TestReentrantLock {

    private static volatile int i = 0;

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        lock.lock();    //在此打断点
        i++;
        lock.unlock();
    }
}

首先,关于重要方法的类的继承关系图如下

多线程学习--关于ReentrantLock源码详细解读

整体方法调用流程如下图

多线程学习--关于ReentrantLock源码详细解读

由lock.lock()方法进入到ReentrantLock类中,得到lock()方法的实现其实是调用了sync.lock();此方法是由Sync的子类NonfairSync实现的。源码如下

final void lock() {
   if (compareAndSetState(0, 1))
        //如果抢到锁,设置为私有
       setExclusiveOwnerThread(Thread.currentThread());
   else
        //没抢到锁,进入等待队列
       acquire(1);
}

通过CAS来获取锁,如果抢到锁,就设为私有,由此可见ReentrantLock是排他锁;

如果没抢到,则进入等待队列;我们来继续看看acquire()方法,源码如下

//tryAcquire(arg):
//acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
//addWaiter()
//没抢到锁,并且加入到等待队列的尾部
if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
     selfInterrupt();

其中条件中有三个方法,依次为tryAcquire()、acquireQueued()和addWaiter();

1.先看tryAcquire()方法,见名知意尝试获得,尝试去获得锁。它是通过ReentrantLock类中的内部类NofairSync实现的;调用了nonfairTryAcquire()方法;源码如下

        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) { //如果没加锁
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);  //抢到锁,设置私有
                    return true;
                }
            }
            //有锁,是自己线程的,重入,c+acquires
            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;
        }

由源码可知,如果是第一次获得锁,那就设为私有;如果此线程已经抢到锁,那么就将state状态修改,c+acquires,即+1,由此可见ReentrantLock是可重入锁

2.再看addWaiter()方法,关键,源码如下

    private Node addWaiter(Node mode) {
        //node为当前线程
        Node node = new Node(Thread.currentThread(), mode);
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

prev:代表前一个节点

next:代表后一个节点

tail:代表尾部节点

由if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))理解,如果!tryAcquire(arg)为true,即没获取到锁,那么将进入等待队列。

addWaiter()即将此节点加入到等待队列的尾部;node为当前节点,将原队列的尾部节点赋值给pred节点,如果不为空,则将node节点的前一个节点指向pred(即原队列的尾部节点)

然后进行CAS进行设置,如果没有其他节点加入进来,那么此节点加入成功,pred(即原尾部节点)的下一个节点指向node,即将node加入到了原队列的尾部

3.acquireQueued()方法,获得等待队列;源码如下

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();  //node的前任节点
                if (p == head && tryAcquire(arg)) {  //前任节点是头节点,并且得到锁
                    setHead(node);                              //将node设置为头节点
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

shouldParkAfterFailedAcquire()方法源码如下:

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            return true;
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

parkAndCheckInterrupt()方法源码如下:

    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

关于ReentrantLock源码的简单理解,希望各路大神多多指点。

本文地址:https://blog.csdn.net/mrcool2012/article/details/109851063