ReentramLock源码分析

之前总结过AQS相关的源码,但是格式比较乱,没法看,有些问题也不是很清楚,这段沉淀时间再瞻仰一次

大家推荐个靠谱的公众号程序员探索之路,大家一起加油 https://img-blog.csdnimg.cn/20181129224604602.png

目录

AQS属性/结构

属性:

Node介绍

从ReentranLock看AQS

ReentramLock构造函数

非平锁

类关系图:

实例方式:

抢锁(没有设置超时时间):

释放锁:

总结:

抢锁(设置超时时间)

公平锁

类关系图:

实例方式:

抢锁(没有设置超时时间)


从ReentranLock看AQS

ReentramLock构造函数

从构造函数可以看出,默认为非公平锁,传true为公平锁

 

public ReentrantLock() {

    sync = new NonfairSync();

}

 

/**

 * Creates an instance of {@code ReentrantLock} with the

 * given fairness policy.

 *

 * @param fair {@code true} if this lock should use a fair ordering policy

 */

public ReentrantLock(boolean fair) {

    sync = fair ? new FairSync() : new NonfairSync();

}

非平锁

类关系图:

实例方式:

ReentrantLock reentrantLock = new ReentrantLock(false);
或者
ReentrantLock reentrantLock = new ReentrantLock();

抢锁(没有设置超时时间):

rentrantLock.lock();

位置:ReentrantLock 205
方法流程:
直接cas设置锁状态如果成功 设置当前持有锁的线程
如果失败acquire(1) 也就是抢锁
final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

compareAndSetState(0, 1) setExclusiveOwnerThread(Thread.currentThread() 方法不再分析

acruire(1)

位置:AbstractQueuedSynchronizer 1197
方法流程:
  1. 竞争锁 上面不是竞争失败了吗?怎么还竞争,万一此时释放了呢
  2. 如果竞争锁失败  添加在阻塞队列中
  3. 如果竞争锁和添加在阻塞队列中都失败 中断该线程就是设置中断标志位
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

tryAcquire(arg)

位置:ReentrantLock 212
protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

nonfairTryAcquire(acquires)

位置:ReentrantLock 129
方法流程:
  1. 如果当前锁状态为0 表示没有线程占用该锁 那么就cas抢占 设置当前持有锁的线程 返回true
  2. 如果不是0,但是占用锁的线程是当前线程,那就是重入 把锁状态 +1不需要cas 返回true
  3. 如果上面两个都不成立 返回false
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
//当前锁状态为0
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
//占用锁的线程是当前线程
    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;
}

acquireQueued(addWaiter(Node.EXCLUSIVE), arg)

addWaiter(Node.EXCLUSIVE)

位置:AbstractQueuedSynchronizer 605
方法目的:阻塞队列初始化(如果没有的话),将当前线程加入到阻塞队列中 返回添加到队尾的node
方法流程:
  1. 如果队尾不为null,说明已经初始化过,将node加入到队尾即可,注意这里细节,node的前驱为队尾,然后cas设置node为队尾 如果成功设置原队尾的next为node(现在的队尾) 为什么cas设置 因为有并发的可能
  2. 如果队尾为null 说明没有初始化阻塞队列,进行初始化,把node加入到队尾
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;
}

enq(node)

位置:AbstractQueuedSynchronizer 583
方法目的:阻塞队列初始化(如果没有的话),将当前线程加入到阻塞队列中
方法流程:
  1. 如果队尾为null 设置队头为空node 队尾 = 队头
  2. 自旋设置node为队尾节点
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        //如果队尾为null
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
        //设置node为队尾节点
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

acquireQueued

位置:AbstractQueuedSynchronizer 857
方法目的:
方法流程:
  1. 如果node的前驱是head 而且抢到锁 那么 node设置为head
  2. 如果上面条件不符合 把node前驱的waitStatus设置SIGNAL 挂起node所在的线程
  3. 如果上面出现任何异常 取消节点
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            //如果node的前驱是head 而且抢到锁
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //一般判断这个节点需不需要挂起 第一次都是返回false 因为只有
添加节点了  才会把前驱节点的等待状态设置为
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        //取消操作后面分析
        if (failed)
            cancelAcquire(node);
    }
}

shouldParkAfterFailedAcquire(p, node)

位置:AbstractQueuedSynchronizer 795
方法目的:判断当前线程是否需要挂起
方法流程:

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;

/**

大于0表示 前驱节点已经取消了(可以看看前面node状态的取值)

那就往前找前驱 直到 不是大于0的

*/

 

    if (ws > 0) {

        /*

         * Predecessor was cancelled. Skip over predecessors and

         * indicate retry.

         */

        do {

            node.prev = pred = pred.prev;

        } while (pred.waitStatus > 0);

        pred.next = node;

    } else {

    /**这种情况就是把 前驱的waitStatuscas为SIGNAL就行了

 

        /*

         * waitStatus must be 0 or PROPAGATE.  Indicate that we

         * need a signal, but don't park yet.  Caller will need to

         * retry to make sure it cannot acquire before parking.

         */

        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);

    }

    return false;

}

parkAndCheckInterrupt()

位置:AbstractQueuedSynchronizer 795
方法目的:将当前线程挂起,返回当前线程中断状态
方法流程:

private final boolean parkAndCheckInterrupt() {

    LockSupport.park(this);

    return Thread.interrupted();

}

 

大致流程:设置锁状态 成功 就已经抢到锁了;失败,进入阻塞队列

取消节点

需要干什么事,假设需要取消的节点为node,如果node的前驱不是取消,那么只需要修改指针略过node即可,如果不是需要处理

 

位置:AbstractQueuedSynchronizer 742
方法目的:取消节点
方法流程:

private void cancelAcquire(Node node) {

    // 忽略不存在的node

    if (node == null)

        return;

 

    node.thread = null;

 

    // Skip cancelled predecessors

    Node pred = node.prev;

    //如果node的前驱 是取消的节点 那么一直往前找 直到找到没有取消的节点

    while (pred.waitStatus > 0)

        node.prev = pred = pred.prev;

 

    // predNext is the apparent node to unsplice. CASes below will

    // fail if not, in which case, we lost race vs another cancel

    // or signal, so no further action is necessary.

    Node predNext = pred.next;

 

    // Can use unconditional write instead of CAS here.

    // After this atomic step, other Nodes can skip past us.

    // Before, we are free of interference from other threads.

    node.waitStatus = Node.CANCELLED;

 

    // 如果node是队尾 那么直接把 前驱的后继设置为null即可.

    if (node == tail && compareAndSetTail(node, pred)) {

        compareAndSetNext(pred, predNext, null);

    } else {

        // If successor needs signal, try to set pred's next-link

        // so it will get one. Otherwise wake it up to propagate.

        int ws;

//如果 pred不是头结点  而且 pred的waitStatus是允许唤醒后继的那么就将node略过

        if (pred != head &&

            ((ws = pred.waitStatus) == Node.SIGNAL ||

             (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&

            pred.thread != null) {

            Node next = node.next;

            if (next != null && next.waitStatus <= 0)

                compareAndSetNext(pred, predNext, next);

        } else {

//如果pred是头结点  或者 pred的waitStatus是不允许唤醒后继

pred的next是node node还取消

所以直接唤醒 node的next就ok了

            unparkSuccessor(node);

        }

 

        node.next = node; // help GC

    }

}

 

释放锁:

rentrantLock.unlock();

 

public void unlock() {

    sync.release(1);

}

 

release(1)

位置:AbstractQueuedSynchronizer 1260
方法目的:释放锁 如果阻塞队列已经初始化 而且头结点的waitStatus不为0
唤醒线程

public final boolean release(int arg) {

    if (tryRelease(arg)) {

        Node h = head;

        if (h != null && h.waitStatus != 0)

            unparkSuccessor(h);

        return true;

    }

    return false;

}

tryRelease(arg)

位置:ReentrantLock 148
方法目的:释放锁

方法流程:

  1. C = 当前锁状态 – 1
  2. 如果c == 0锁无线程使用 fanhuitrue
  3. 如果 不是 说明锁有重入情况 特殊处理

protected final boolean tryRelease(int releases) {

     //计算释放锁后的锁状态

    int c = getState() - releases;

    if (Thread.currentThread() != getExclusiveOwnerThread())

        throw new IllegalMonitorStateException();

    boolean free = false;

//如果c == 0

    if (c == 0) {

        free = true;

        setExclusiveOwnerThread(null);

    }

    setState(c);

    return free;

}

unparkSuccessor(h)

位置:AbstractQueuedSynchronizer 148
方法目的:唤醒 head的后继节点

方法流程:

private void unparkSuccessor(Node node) {

    /*

     * If status is negative (i.e., possibly needing signal) try

     * to clear in anticipation of signalling.  It is OK if this

     * fails or if status is changed by waiting thread.

     */

    int ws = node.waitStatus;

    //这里为什么要用cas呢? 因为tryRelease已经把锁状态改变了呀

    if (ws < 0)

        compareAndSetWaitStatus(node, ws, 0);

 

    //这里为什么循环呢 因为后继节点有可能取消了 那就从后往前找找到第一个   没有取消的节点

    Node s = node.next;

    if (s == null || s.waitStatus > 0) {

        s = null;

        for (Node t = tail; t != null && t != node; t = t.prev)

            if (t.waitStatus <= 0)

                s = t;

    }

    //唤醒 s节点的线程 然后接着上面的挂起地方继续执行 抢锁。。。。

    if (s != null)

        LockSupport.unpark(s.thread);

}

大致流程:释放锁,唤醒后继节点

总结:

主要通过四个部件协调并发

  1. 锁状态 private volatile int state 来控制是否抢到锁,释放锁
  2. 节点等待状态 waitStatus
  3. 挂起,唤醒线程方法

LockSupport.park(this);

LockSupport.unpark(s.thread);

  1. 阻塞队列,将挂起的线程排队

抢锁(设置超时时间)

除了加了一个判断超时间的逻辑其他地方都一样

使用示例:

reentrantLock.tryLock(1000, TimeUnit.SECONDS)

tryLock(1000, TimeUnit.SECONDS)

位置:ReentrantLock 440

public boolean tryLock(long timeout, TimeUnit unit)

        throws InterruptedException {

    return sync.tryAcquireNanos(1, unit.toNanos(timeout));

}

位置:AbstractQueuedSynchronizer 1242 

方法流程:tryAcquire(arg)和上面分析的一样就不分析了

public final boolean tryAcquireNanos(int arg, long nanosTimeout)

        throws InterruptedException {

    if (Thread.interrupted())

        throw new InterruptedException();

    return tryAcquire(arg) ||

        doAcquireNanos(arg, nanosTimeout);

}

doAcquireNanos(arg, nanosTimeout)

位置:AbstractQueuedSynchronizer 913
方法目的:在限定的时间内获取锁

方法流程:其实除了增加一个是否超时

private boolean doAcquireNanos(int arg, long nanosTimeout)

        throws InterruptedException {

    if (nanosTimeout <= 0L)

        return false;

    final long deadline = System.nanoTime() + nanosTimeout;

    final Node node = addWaiter(Node.EXCLUSIVE);

    boolean failed = true;

    try {

        for (;;) {

            final Node p = node.predecessor();

            if (p == head && tryAcquire(arg)) {

                setHead(node);

                p.next = null; // help GC

                failed = false;

                return true;

            }

            nanosTimeout = deadline - System.nanoTime();

//如果 超时了 直接返回false 会走到下面的 finally块中 此时 failed = true 所以 会取消节点

            if (nanosTimeout <= 0L)

                return false;

            if (shouldParkAfterFailedAcquire(p, node) &&

                nanosTimeout > spinForTimeoutThreshold)

                LockSupport.parkNanos(this, nanosTimeout);

            if (Thread.interrupted())

                throw new InterruptedException();

        }

    } finally {

        if (failed)

            cancelAcquire(node);

    }

}

 

公平锁

类关系图:

实例方式:

ReentrantLock reentrantLock = new ReentrantLock(true);

抢锁(没有设置超时时间)

其实就是获取锁的时候和 非公平锁有区别 其他的没有

位置:ReentrantLock 223
方法目的: 

方法流程:

final void lock() {

    acquire(1);

}

位置:ReentrantLock 231

方法目的:

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;

}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值