【JUC系列第五篇】-ReentrantLock锁原理解读

  1. 云栖社区>
  2. 博客>
  3. 正文

【JUC系列第五篇】-ReentrantLock锁原理解读

毕来生 2019-03-01 09:24:27 浏览371
展开阅读全文

作者 : 毕来生


锁状态转换

在这里插入图片描述

Lock分类

​ Jdk1.5以后帮助我们提供了线程同步机制,通过显示定义同步锁来实现对象之间的同步。还是Doug Lea这个家伙写的。相信读过源码的人在很多地方都可以看到这个家伙。

​ Lock可以显示的进行加锁,解锁。但是每次只能有一个线程对Lock对象加锁

​ Lock实现结构如下图所示:

​ 按照使用的常用度,分别标注了(1),(2),(3)。接下来我们就主要学习一下ReentrantLock的使用

在这里插入图片描述

可重入锁

ReentrantLock实现的前提就是AbstractQueuedSynchronizer,简称AQS.。核心方法内部实现均在AQS中,后续我们在详细解读AQS相关知识点以及使用场景。我们先来看一段伪代码用以表述可重入锁的使用情况。接下来我们来详细分析获取锁以及释放锁内部实现到底做了什么事情。

package org.bilaisheng.juc;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author: bilaisheng
 * @Wechat: 878799579
 * @Date: 2019/1/3 22:55
 * @Todo: 伪代码仅演示使用
 * @Version : JDK11 , IDEA2018
 */
public class ReentrantLockTest {


    public static void main(String[] args) {
        Lock lock = new ReentrantLock();

        // 获取锁
        lock.lock();
        
        // access the resource protected by this lock
        // do something
        
        // 释放锁
        lock.unlock();

    }
}

Sync对象剖析

     /** Synchronizer providing all implementation mechanics */
    private final Sync sync;

    /**
     * Base of synchronization control for this lock. Subclassed
     * into fair and nonfair versions below. Uses AQS state to
     * represent the number of holds on the lock.
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        // xxxx
    }    

他会根据传入构造方法的布尔类型参数实例化出Sync的实现类FairSync和NoFairSync。

  • FairSync: 公平的Sync
  • NoFairSync : 不公平的Sync
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

ReentrantLock.lock()实现原理

​ 我们用的比较多的ReentrantLock是非公平锁,我们来用一张图来分析一下看看它是如何实现的。在这里插入图片描述


上图就做了两件事情:

1、设置AbstractQueuedSynchronizer的state为1

2、设置AbstractOwnableSynchronizer的thread为当前线程

线程A正在执行中,status = 1。

线程B尝试利用CAS去判断state是不是0,是0就设置为1,当然这一步操作肯定是失败的,因为线程A已经将state设置成了1,所以此时肯定是失败的。

失败了之后进入FIFO等待队列。等待重新尝试

/**
 * Performs non-fair tryLock.  tryAcquire is implemented in
 * subclasses, but both need nonfair try for trylock method.
 */
@ReservedStackAccess
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    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;
}

因为在AbstractQueuedSynchronizer中state是用volatile关键字声明的,故可以在线程间可见

/**
 * The synchronization state.
 */
private volatile int state;

再次判断一下能否持有锁(可能线程A同步代码执行得比较快,已经释放了锁),不可以就返回false。

根据上方代码可以看出同一个锁最多能重入Integer.MAX_VALUE次,也就是2147483647。

ReentrantLock.unLock()实现原理

此处较为简单。附上调用关系链路

// 步骤一
public void unlock() {
    sync.release(1);
}

// 步骤二 : AbstractQueuedSynchronizer
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
           unparkSuccessor(h);
        return true;
    }
    return false;
}

//步骤二 : ReentrantLock
 @ReservedStackAccess
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

当一条线程对同一个ReentrantLock全部解锁之后,AQS的state就是0了,AbstractOwnableSynchronizer的exclusiveOwnerThread将被设置为null,这样就表示没有线程占有锁,方法返回true。

喜欢就关注我吧

在这里插入图片描述

网友评论

登录后评论
0/500
评论
毕来生
+ 关注