首页 > 编程语言 >从源码入手详解ReentrantLock,一个比synchronized更强大的可重入锁

从源码入手详解ReentrantLock,一个比synchronized更强大的可重入锁

时间:2024-04-21 11:45:43浏览次数:33  
标签:重入 synchronized ReentrantLock 获取 源码 线程 公平

写在开头

随手一翻,发现对于Java中并发多线程的学习已经发布了十几篇博客了,多线程 是Java基础中的重中之重!因此,可能还需要十几篇博客才能大致的讲完这部分的知识点,初学者对于这部分内容一定要多花心思,不可马虎!今天我们继续来学习一个重要知识点:ReentrantLock

ReentrantLock :是一种独占式的可重入锁,位于java.util.concurrent.locks中,是Lock接口的默认实现类,底部的同步特性基于AQS实现,和synchronized关键字类似,但更灵活、功能更强大、也是目前实战中使用频率非常高的同步类。

几种不同锁的定义

在学习ReentrantLock之前,我们先来复习一下如下的几类锁的定义,这个其实很早的博文中就已经详细的整理过了,这里为了更好理解ReentrantLock锁,还是简单罗列一下。

独占锁与共享锁

  1. 独占锁:同一时间,一把锁只能被一个线程获取;
  2. 共享锁:同意时间,一把锁可以被多个线程获取。

公平锁与非公平锁

  1. 公平锁:按照申请锁的时间先后,进行锁的再分配工作,这种锁往往性能稍差,因为要保证申请时间上的顺序性;
  2. 非公平锁: 锁被释放后,后续线程获得锁的可能性随机,或者按照设置的优先级进行抢占式获取锁。

可重入锁

所谓可重入锁就是一个线程在获取到了一个对象锁后,线程内部再次获取该锁,依旧可以获得,即便持有的锁还没释放,仍然可以获得,不可重入锁这种情况下会发生死锁!

可重入锁在使用时需要注意的是:由于锁会被获取 n 次,那么只有锁在被释放同样的 n 次之后,该锁才算是完全释放成功。

可中断锁与不可中断锁

  1. 可中断锁:在获取锁的过程中可以中断获取,不需要非得等到获取锁后再去执行其他逻辑;
  2. 不可中断锁:一旦线程申请了锁,就必须等待获取锁后方能执行其他的逻辑处理。

ReentrantLock是一种同时拥有独占式、可重入、可中断、公平/非公平特性的同步器!


ReentrantLock

根据上面总结出的特点,我们从底层源码出发来验证一下结论的准确性,首先我们通过一个关系图谱来大致梳理一下ReentrantLock的内部构造。

image

ReentrantLock实现了Lock和Serializable接口:

public class ReentrantLock implements Lock, java.io.Serializable {}

其内部拥有三个内部类,分别为Sync、FairSync、NonfariSync,其中FairSync、NonfariSync继承父类Sync。Sync又继承了AQS(AbstractQueuedSynchronizer),添加锁和释放锁的大部分操作实际上都是在 Sync 中实现的。

问题1:ReentrantLock内部公平锁与非公平锁如何实现?

在内部通过构造器来实现公平锁与非公平锁的设置,默认为非公平锁,同样可以通过传参设置为公平锁。底层实现其实是通过FairSync、NonfariSync这个两个内部类,源码如下:

//无参构造,默认为非公平锁
public ReentrantLock() {
    sync = new NonfairSync();
}
// 传入一个 boolean 值,true 时为公平锁,false 时为非公平锁
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

问题2:独占锁如何实现?

在源码中无论是Sync这个内部类或是其子类,都会调用setExclusiveOwnerThread(current)这个方法,这个方法是AQS的父类AOS(AbstractOwnableSynchronizer)中的方法,用以标记锁的持有者为独占模式。

image

问题3:ReentrantLock如何获取和释放锁?

由于ReentrantLock是默认非公平锁,所以我们就以非公平模式为例去看一下它底层如何实现锁的获取与释放的。

1️⃣ 锁的获取

核心方法为Sync内部类的nonfairTryAcquire方法,如下为其源码,先获取当前锁的状态,若为0说明没有被任何线程获取,此时直接获取即可;另外一种state不为0时,则需要判断占有线程是否为当前线程,若是则可以获取,并将state值加一返回,否则获取失败。

【注意】:公平模式下获取锁的时会多一步调用hasQueuedPredecessors 的逻辑判断,用以判断当前线程对应的节点在等待队列中是否有前驱节点,毕竟公平锁的竞争严格按照获取锁的时间进行分配的。

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    //1. 如果该锁未被任何线程占有,该锁能被当前线程获取
	if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
	//2.若被占有,检查占有线程是否是当前线程
    else if (current == getExclusiveOwnerThread()) {
		// 3. 再次获取,计数加一
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

2️⃣ 锁的释放

对应的以非公平锁中释放为例,通过源码我们可以看到,每调用一次则同步状态减1,直至同步状态为0,锁才被完全的释放完,否则返回false。

protected final boolean tryRelease(int releases) {
	//1. 同步状态减1
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
		//2. 只有当同步状态为0时,锁成功被释放,返回true
        free = true;
        setExclusiveOwnerThread(null);
    }
	// 3. 锁未被完全释放,返回false
    setState(c);
    return free;
}

3️⃣ 小总结

经过上面源码的学习,我们已经能够确认一点就是:ReentrantLock是一种同时拥有独占式、可重入、可中断、公平/非公平特性的同步器!我们接下来就继续再来学习一下它的使用。

问题4:ReentrantLock的使用

我们通过一个小demo,来感受一下基于非公平锁模式下的ReentrantLock的使用哈

public class Test {
    //初始化一个静态lock对象
    private static final ReentrantLock lock = new ReentrantLock();
    //初始化计算量值
    private static int count;

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(()->{
            for (int i = 0; i <1000 ; i++) {
                lock.lock();
                try {
                    count++;
                } finally {
                    lock.unlock();
                }
            }
        });
        Thread thread2 = new Thread(()->{
            for (int i = 0; i < 1000; i++) {
                lock.lock();
                try {
                    count++;
                } finally {
                    lock.unlock();
                }
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("result:"+count);
    }
}

上面这个程序预期输出结果为:2000,thread1和thread2分别做了加1000次的操作,由于ReentrantLock是独占式可重入锁,故最终可以成功打印出预期结果!

结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

image

如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

image

标签:重入,synchronized,ReentrantLock,获取,源码,线程,公平
From: https://www.cnblogs.com/JavaBuild/p/18148726

相关文章

  • 【记一个小坑】linux源码在window10使用过程中的坑
    在window10解压linux.tar.gz的时候遇到了一下几个问题:(我用的解压软件是Bandzip)1、解压的时候会有如下图的提示,网上有一些人说不用管,直接覆盖,但是并不是所有人都适用,如果你是想要修改linux源码,然后编译安装的话,直接覆盖,之后会造成编译的时候报错:找不到XXX文件原因是windows默......
  • PostgreSQL源码编译安装指南
    一、版本说明centos7.9postgresql12.2二、配置系统基本环境#1.创建postgres用户groupaddpostgresuseradd-gpostgrespostgresecho"666666"|passwdpostgres--stdin#2.配置用户环境变量su-postgrescat>>~/.bash_profile<<EOFexportPGPORT=1922expor......
  • 短视频源码,高效异常处理让您的代码更完美
    在编写Python代码的过程中,我们常常会遇到各种错误和异常。学会有效地处理这些异常,可以让短视频源码更加健壮、稳定。本文将带您深入了解Python异常处理的艺术,让您的短视频源码更加完美!一.异常简介异常是在短视频源码执行过程中发生的事件,通常表示代码中存在错误或异常情况......
  • 企业微信hook 最新版 、企微输入验证码,二次扫码方案、发名片收消息功能,企业微信hook源
    ​产品说明在PC端企业微信客户端上发送自定义名片、封装企业微信功能为DLL,可与其他语言调用实现功能。DLL可以监听企业微信的所有消息接收和群消息,根据需求实现机器人、群发、自动消息推送、聊天机器人、监管数据收集等功能企业微信hook,企业微信功能api,自定义开发功能清......
  • 马扎克,海德汉,哈斯,兄弟,发那科,三菱,西门子,华中数控,knd,广数,宝元,发格,无授权源码采集。机床
    机床联网cnc采集设备联网车间数字化生产追踪 无需授权可跨平台运行任何平台Linuxwindows等 可+:cnccaiji机床数据采集MDC,DNC,可定制开发,有采集驱动支持多品牌cnc系统多设备采集支持转发mqtt推送HTTP马扎克机床数据采集海德汉机床数据采集哈斯机床数据采集......
  • Spring 源码阅读(一)环境搭建
    注意事项:使用2024-03-14发布的Spring5.3.33版本IDE工具使用了IntellijIDEA,同时为了简化不必要的内容没单独配置Gradle环境JDK版本采用EclipseTemurin1.8/11均可下载源码下载SpringFramework源码,本次选择5.3.33版本,发布日期2024-03-14,通过IntellijIDE......
  • LibreCAD源码编译过程记录
    获取源码远程仓库https://github.com/LibreCAD/LibreCAD.git将源码从GitHub克隆到本地将仓库源码检出到提交节点:4b91d9b0f919be41f7e7568c87c5c67dfac189aa,这是LibreCADv2.2.0稳定版的提交节点,如果用更新的普通节点有可能编译出错(已踩坑)部署编译环境Qt5.9.7注意......
  • 09_QT系统移植之交叉编译QT源码和触摸源码
    QT系统移植之交叉编译QT源码和触摸源码交叉编译QT源码一.源码下载​QT官网下载:http://download.qt.io/new_archive/qt/5.7/5.7.1/二.编译环境开发环境:Ubuntu16编译器:arm-2014.05-29-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2三.编译触摸触摸源码在这个视频目录......
  • 圈子社交系统--在线了解前后端,APP小程序H5,三端源码交付-多重玩法,新奇有趣。
    圈子论坛社区系统,含完整的后台PHP系统。功能:小程序授权登陆,H5和APP,手机号登陆,发帖,建圈子、发活动。圈主可置顶推荐帖子,关注、点赞、评论、交流等。可作为圈子贴吧等自媒体。一款全开源支持免费商用的圈子社区论坛系统;系统基于TP6+Uni-app框架开发;客户移动端采用uni-app开发,管理......
  • winform车牌识别源码(纯算法)
    车牌识别,本是图像领域中,非常成熟的一个应用,也是目前无处不在的停车场自动收费设备的技术基础。前言本文将使用c#语言,winform框架开发一个车牌识别系统M=,不借助任何框架,纯算法。效果  使用工具VisualStudio2019思路打开要识别的车牌对车牌进行去雾操作接着进......