我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java多线程并发之ReentrantLock

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java多线程并发之ReentrantLock

ReentrantLock

公平锁和非公平锁

这个类是接口 Lock的实现类,也是悲观锁的一种,但是它提供了 lockunlock方法用于主动进行锁的加和拆。在之前使用的 sychronized关键字是隐式加锁机制,而它是显示加锁,同时,这个类的构造方法提供了公平和非公平的两种机制。

什么是公平和非公平呢?就是多线程对共享资源进行争夺的时候,会出现一个线程或几个线程完全占有共享资源,使得某些线程在长时间处于等待状态。公平就是要等待时间过长的线程先获得锁。

而在 ReentrantLock类中,提供了公平锁和非公平锁的使用。

ReentrantLock源码中,构造器提供了一个参数入口,

public ReentrantLock(boolean fair) {
	sync = fair ? new FairSync() : new NonfairSync();
}

当fair为true的时候,会创造一个 FairSync对象给 sync属性,FairSync是继承自 Sync的类,其中有一个 Lock方法,而在 ReentrantLock的Lcok中使用的是 sync属性的 Lock方法,故能够保证“公平”。

使用非公平锁就不需要在构造器中传参数。

在使用的时候,需要手动上锁和解锁。

使用公平锁,会将占优势的线程进行限制,恢复挂起的线程,但是这个过程在CPU层面来讲,是存在明显时间差异的,非公平锁的执行效率相对更高,所以一般来说不建议使用公平锁,除非现实业务上需要符合实际需求。

重入锁

ReentrantLock本身还支持重入的功能。

重入锁(Reentrant Lock)是一种支持重入的独占锁,它允许线程多次获取同一个锁,在释放锁之前必须相应地多次释放锁。重入锁通常由两个操作组成:上锁(lock)和解锁(unlock)。当一个线程获取了重入锁后,可以再次获取该锁而不被阻塞,同时必须通过相同数量的解锁操作来释放锁。

重入锁具有如下特点:

  • 重入性:重入锁允许同一个线程多次获取同一把锁,避免了死锁的发生。
  • 独占性:与公平锁和非公平锁一样,重入锁也是一种独占锁,同一时刻只能有一个线程持有该锁。
  • 可中断性:重入锁支持在等待锁的过程中中断该线程的执行。
  • 条件变量:在使用 java.util.concurrent.locks.Condition 类配合重入锁实现等待/通知机制时,等待状态总是与重入锁相关联的。

重入锁相对于 synchronized 关键字的优势在于,重入锁具有更高的灵活性和扩展性,支持公平锁和非公平锁、可中断锁和可轮询锁等特性,能够更好地满足多线程环境下的并发控制需要。synchroized也有重入性。

ReentrantLock lock = new ReentrantLock(true);
public void get(){
    while(true){
        try{
            lock.lock();
            lock.lock();
        }catch(Exception exception){

        }finally{
            lock.unlock();
            lock.unlock();
        }
    }
}

可重入的前提 lock是同一个对象,而关键字 synchroized的 Monitor也是同一个对象充当,才能判定为重入。

public void get(){
    while(true){
        synchronized(this){
            System.out.println("外层");
            synchronized(this){
                System.out.println("内层");
            }
        }
    }
}

那么Java是怎么检测锁的重入和获取锁的次数的呢?在之前说过的 ObjectMobitor的C++源代码中有 _recursions和_count来记录锁的重入次数和线程获取锁的次数。这样在Java层面就表示一个锁对象都拥有一个锁计数器 _count和一个指向持有这个锁的线程的指针 _owner只有当前持有锁的线程才能使得计数器+1,其他线程只有等待锁被释放(计数器置0)才能持有并+1。

在源码中,非公平锁的lock方法如下:

//ReentrantLock类中:
final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}
//0的参数为是expect,是期望值,而1是update,是更新值

在执行comparaAndSetState方法的时候,它会询问锁的计数器(在底层执行compareAndSwapInt的本地方法),并期望数值为0,如果为0返回true,然后设置执行线程主是当前线程。如果非0,那么他就会执行acquire

//AbstractQueuedSynchronizer类中:
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
//这里的tryAcquire,需要在其继承的子类中进一步实现对应的功能
//子类可以根据自己的需要重新定义tryAcquire(int arg)的实现方式,从而实现更优秀的锁控制方案:
//而在其子类FairSync中便覆盖了这个方法
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}

将线程放入等待队列。

同时计数器是通过 unlock来-1,所以 lock和unlock次数不匹配就会产生死锁,也就是当两个线程调用同一个 ReentrantLock如果一个线程中的上锁解锁次数不相等,那么计数器没有被清零,当另一个线程请求锁的时候,看到锁计数器不是0,就认为被的线程仍然持有它,所以一直等待它被释放。需要了解底层的可以去看AQS中的release方法。

而在 ReentrantLock中有一个抽象内部类 Sync,它继承自抽象类AbstractQueuedSynchronizer(简称AQS),这个类中有一个内部 Node类,当有线程等待这把锁的时候,会创建一个等待队列,放置这些处于等待的线程。(AQS实现比较复杂,有兴趣可以看看“竹子爱熊猫”大佬的文章。)

小结

ReentrantLock类中,有内部类三个,Sync,FairSync,NonfairSync,他们的关系是Sync是后两个的父类,后两个是兄弟类,同时Sync继承自AQS类,在AQS中有很多实现公平和非公平、可重入的机制,而具体实现效果的是Sync,FairSync,NonfairSync

疑惑

在下列代码中,为什么在第一个线程的最后加上.join(),没有使得线程阻塞,而没有它就会阻塞?

Lock lock = new ReentrantLock();
new CompletableFuture().runAsync(() -> {
    lock.lock();
    try{
        System.out.println(1);
        TimeUnit.SECONDS.sleep(2);
    }catch(Exception e){
    }finally{
}});
//上面加上.join()
new CompletableFuture().runAsync(() -> {
    lock.lock();
    try{
        System.out.println(2);
    }catch(Exception e){
    }finally{
        lock.unlock();
}}).join();

到此这篇关于Java多线程并发之ReentrantLock的文章就介绍到这了,更多相关ava  ReentrantLock内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java多线程并发之ReentrantLock

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java多线程并发之ReentrantLock

这篇文章主要介绍了Java 多线程并发ReentrantLock,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下
2023-05-18

Java多线程并发ReentrantLock怎么使用

这篇文章主要介绍“Java多线程并发ReentrantLock怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java多线程并发ReentrantLock怎么使用”文章能帮助大家解决问题。背景
2023-07-02

理解Java多线程之并发编程

这篇文章主要介绍了理解Java多线程之并发编程的相关资料,需要的朋友可以参考下
2023-02-02

【漫画】JAVA并发编程 J.U.C Lock包之ReentrantLock互斥锁

在如何解决原子性问题的最后,我们卖了个关子,互斥锁不仅仅只有synchronized关键字,还可以用什么来实现呢?J.U.C包中还提供了一个叫做Locks的包,我好歹英语过了四级,听名字我就能马上大声的说:Locks包必然也可以用作互斥!ReentrantLo
【漫画】JAVA并发编程 J.U.C Lock包之ReentrantLock互斥锁
2018-12-24

python 并发执行之多线程

正常情况下,我们在启动一个程序的时候。这个程序会先启动一个进程,启动之后这个进程会拉起来一个线程。这个线程再去处理事务。也就是说真正干活的是线程,进程这玩意只负责向系统要内存,要资源但是进程自己是不干活的。默认情况下只有一个进程只会拉起来一
2023-01-31

如何理解 Java 多线程与并发库之间的关系?(java多线程与并发库的关系)

在Java编程中,多线程和并发库是两个重要的概念,它们之间存在着密切的关系。本文将深入探讨Java多线程与并发库的关系,帮助读者更好地理解和应用这两个概念。一、多线程的基本概念多线程是指在一个程序中同时运行多个线程,每个
如何理解 Java 多线程与并发库之间的关系?(java多线程与并发库的关系)
Java2024-12-19

python并发编程之多线程编程

一、threading模块介绍multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍二、开启线程的两种方式方式一: from threading import Threa
2023-01-31

Java多线程中ReentrantLock与Condition有什么用

这篇文章给大家分享的是有关Java多线程中ReentrantLock与Condition有什么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、ReentrantLock类1.1什么是reentrantlock
2023-05-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录