资讯专栏INFORMATION COLUMN

JAVA并发编程之-ReentrantLock锁原理解读

荆兆峰 / 1536人阅读

摘要:作者毕来生微信锁状态转换分类以后帮助我们提供了线程同步机制,通过显示定义同步锁来实现对象之间的同步。等待重新尝试因为在中是用关键字声明的,故可以在线程间可见再次判断一下能否持有锁可能线程同步代码执行得比较快,已经释放了锁,不可以就返回。

作者 : 毕来生
微信: 878799579
锁状态转换

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。

喜欢就关注我吧

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75675.html

相关文章

  • 手撕面试官系列(七):面试必备常问并发编程高级面试专题

    摘要:如何在线程池中提交线程内存模型相关问题什么是的内存模型,中各个线程是怎么彼此看到对方的变量的请谈谈有什么特点,为什么它能保证变量对所有线程的可见性既然能够保证线程间的变量可见性,是不是就意味着基于变量的运算就是并发安全的请对比下对比的异同。 并发编程高级面试面试题 showImg(https://upload-images.jianshu.io/upload_images/133416...

    Charles 评论0 收藏0
  • 后台开发常问面试题集锦(问题搬运工,附链接)

    摘要:基础问题的的性能及原理之区别详解备忘笔记深入理解流水线抽象关键字修饰符知识点总结必看篇中的关键字解析回调机制解读抽象类与三大特征时间和时间戳的相互转换为什么要使用内部类对象锁和类锁的区别,,优缺点及比较提高篇八详解内部类单例模式和 Java基础问题 String的+的性能及原理 java之yield(),sleep(),wait()区别详解-备忘笔记 深入理解Java Stream流水...

    spacewander 评论0 收藏0
  • 后台开发常问面试题集锦(问题搬运工,附链接)

    摘要:基础问题的的性能及原理之区别详解备忘笔记深入理解流水线抽象关键字修饰符知识点总结必看篇中的关键字解析回调机制解读抽象类与三大特征时间和时间戳的相互转换为什么要使用内部类对象锁和类锁的区别,,优缺点及比较提高篇八详解内部类单例模式和 Java基础问题 String的+的性能及原理 java之yield(),sleep(),wait()区别详解-备忘笔记 深入理解Java Stream流水...

    xfee 评论0 收藏0
  • 后台开发常问面试题集锦(问题搬运工,附链接)

    摘要:基础问题的的性能及原理之区别详解备忘笔记深入理解流水线抽象关键字修饰符知识点总结必看篇中的关键字解析回调机制解读抽象类与三大特征时间和时间戳的相互转换为什么要使用内部类对象锁和类锁的区别,,优缺点及比较提高篇八详解内部类单例模式和 Java基础问题 String的+的性能及原理 java之yield(),sleep(),wait()区别详解-备忘笔记 深入理解Java Stream流水...

    makeFoxPlay 评论0 收藏0

发表评论

0条评论

荆兆峰

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<