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

简介: 作者:毕来生 【JUC系列第五篇】-ReentrantLock锁原理解读

作者 : 毕来生


锁状态转换

在这里插入图片描述

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。

喜欢就关注我吧

在这里插入图片描述

目录
相关文章
|
7月前
|
安全 Java 开发者
深入解析ReentrantLock重入锁:Java多线程中的利器
深入解析ReentrantLock重入锁:Java多线程中的利器
459 4
|
6月前
|
设计模式 安全 Java
JUC第十二讲:JUC锁 - 看不懂锁核心类 AQS 原理来打我
JUC第十二讲:JUC锁 - 看不懂锁核心类 AQS 原理来打我
|
9月前
|
存储 SpringCloudAlibaba 安全
JUC并发编程(四):synchronized底层原理和锁升级优化
`synchronized`翻译过来是**同步**的意思,它是Java中一个关键字,是JVM层面提供的同步锁机制,用于保证多线程访问同一资源的可见性、互斥性。即当一个线程已经获取资源锁时,其他试图获取的线程只能等待或者阻塞在那里。
65 0
JUC并发编程(四):synchronized底层原理和锁升级优化
|
算法 Java API
深入浅出理解Java并发AQS的共享锁模式
深入浅出理解Java并发AQS的共享锁模式
95 0
深入浅出理解Java并发AQS的共享锁模式
|
SpringCloudAlibaba 前端开发 Java
JUC系列(二)八个有关锁的问题,让你吃透各种场景
8锁就是关于的锁的八个问题,下面也有四个demo类来阐述各种场景下锁的不同状态
JUC系列(二)八个有关锁的问题,让你吃透各种场景
|
安全 Java
java并发原理实战(9)--手动实现一个可重入锁
java并发原理实战(9)--手动实现一个可重入锁
java并发原理实战(9)--手动实现一个可重入锁
|
设计模式 SpringCloudAlibaba 安全
聊一聊 ReentrantLock 和 AQS 那点事
聊一聊 ReentrantLock 和 AQS 那点事
133 0
聊一聊 ReentrantLock 和 AQS 那点事
|
Java
Java并发编程 - AQS 简介(AbstractQueuedSynchronizer)
Java并发编程 - AQS 简介(AbstractQueuedSynchronizer)
124 0
Java并发编程 - AQS 简介(AbstractQueuedSynchronizer)
|
设计模式 Java
JUC并发编程——AQS源码解读
JUC并发编程——AQS源码解读
150 0
JUC并发编程——AQS源码解读
|
Java
深入浅出ReentrantLock(可重入锁)
深入浅出ReentrantLock(可重入锁)
157 0
深入浅出ReentrantLock(可重入锁)