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

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

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();
    }
}

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

整体方法调用流程如下图

由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源码的简单理解,希望各路大神多多指点。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值