ReentrantLock详解

目录

一、ReentrantLock的含义

二、RerntrantLock当中的常用方法

     ①lock()和unlock()方法

   ②构造方法

   ③tryLock()方法

       tryLock()无参数

        tryLock(timeout,Times)有参数

    ④lockInterruptibly() throws InterruotedException

经典面试问题: 

ReentrantLock和synchronized有什么不同

使用方法上面的区别

①ReentrantLock可以提供公平+非公平两种特性

 ②ReentrantLock的加锁、解锁操作都是需要手动进行,

③synchronized无法提供lock.tryLock()这样的尝试获取锁的特性,而ReentrantLock可以提供。

④ReentrantLock可以提供中断式加锁。 

 ⑤ReentrantLock借助Condition类:可以指定唤醒线程

 锁的实现方式上面的区别

     提供的级别不一样:

       原理不一样: 

      ReentrantLock的原理 


一、ReentrantLock的含义

        ReentrantLock也是Java当中提供的一种锁。这种锁和synchronized类似也可以起到互斥使用,保证线程安全的的作用。

        关于synchronized的作用,已经在这一篇文章当中提及:

        (3条消息) Java对于synchronized的初步认识_革凡成圣211的博客-CSDN博客https://blog.csdn.net/weixin_56738054/article/details/128062475?spm=1001.2014.3001.5501

        但是,仍然在使用的语法上面和synchronized有一些差别。下面,将具体介绍一下ReentrantLock的使用以及ReentrantLock的各个特性。


二、RerntrantLock当中的常用方法

     ①lock()和unlock()方法

        顾名思义,lock()方法就是线程进入同步代码块之后的加锁操作,而unlock()就是需要离开代码块之后的解锁操作。

       在上述代码当中,锁就是lock对象。当多个线程同时尝试调用lock.lock()方法之后,只有其中一个线程可以获得锁,其余线程都需要阻塞等待。当线程执行到unlock()方法之后,说明已经解锁了,其他线程可以继续获取lock。


       但是, 上面的写法不是规范的写方法,规范的写法,应当把lock.unlock()写进finally代码块当中,并且lock.lock()方法需要在try方法上方的第一行

’ 如下图所示:

       原因:

       如果在上述代码当中,出现了if语句,线程进入if语句之后调用了lock.lock()方法加锁成功。但是线程离开if语句的时候,没有调用lock.unlock()方法,这样也就意味着线程提前返回了,没有解锁。那么其他线程如果有阻塞等待的,将一直阻塞等待。   

       因此,为了防止忘记解锁的情况,应当把lock.unlock()放到finally当中。

       如图:故意不解锁,看看有什么后果。场景,此时有两个线程,一个是thread1,另外一个是thread2。两个线程分别通过同一个对象count调用add()方法:

      运行:

     

可以看到,控制台始终输出了-->"现在的线程是....thread1...",说明thread2无法再次获取到锁。


   ②构造方法

ReentrantLock lock1=new ReentrantLock(true);ReentrantLock lock=new ReentrantLock(false);

       如果构造方法当中,指定了true作为参数,那么lock将是公平锁。如果没有指定布尔值,或者指定了布尔值为false,那么lock将是非公平锁。


   ③tryLock()方法

       tryLock()无参数

        tryLock()方法有两个作用:

       当调用lock.tryLock()的时候,如果lock此时还没有被其他线程占有,那么它会立刻获取到锁,并且返回true。

        如果lock已经被其他线程占用了,那么调用lock.tryLock()的线程将不会阻塞等待,而是继续往下执行。       


        tryLock(timeout,Times)有参数

         当线程调用lock.tryLock(timeout,TimeUtil.时间单位常量)

         方法的时候,会发生以下的情况:

         (1)当前线程将会在lock.tryLock(timeout,TimeUtil.时间单位常量)这行代码处阻塞等待timeout时间,如果获取到锁的线程在这个timeout时间内释放锁了,那么正在等待的线程可以重新获取锁。

         (2)如果阻塞等待的线程直到timeout时间了,加锁的线程仍然没有释放锁,那么原来在等待的线程将不再等待,直接返回。

        (3)如果超时等待的线程在等待锁释放的timeout时间内被中断(其他线程调用t.interrupt())方法中断正在等待的线程,那么当前正在等待的线程会抛出InterruptException,也会终止等待

        因此,正确使用tryLock()的方式为:首先进行判断,如果得到结果为false,也就是获取不到锁,直接return返回即可。

        


    ④lockInterruptibly() throws InterruotedException

      这个方法,类似于"lock"也是属于"加锁"的方法。 

      和单纯的lock()方法不同,线程调用了lockInterrupt()方法之后,可以"响应中断"式地加锁。

      假设,在某一时刻,t1线程获取到锁,在t1调用lockInterruptibly()方法获取到锁之后,如果t2也调用这个方法获取锁,那么t2会进入阻塞等待的状态。

      如果t2在阻塞等待的过程当中,被其他线程调用t2.interrupt()方法,那么线程t2会被触发异常(InterruptException),并且被"唤醒"。


     代码实现:

     add()方法,使用sleep(1000)的目的是减慢循环的速度

class Count1{public int number;ReentrantLock lock=new ReentrantLock();public void add(){try {//使用"可中断"式地加锁lock.lockInterruptibly();//标志位默认为falsewhile (true){number++;Thread.sleep(1000);System.out.println(Thread.currentThread().getName());}} catch (InterruptedException e) {e.printStackTrace();}finally {lock.unlock();}}
}

      启动t1,t2线程,让t1先获取到锁,t2后面才获取到锁:

public class ThreadDemo32 {public static void main(String[] args) {Count1 count1=new Count1();Thread t1=new Thread(new Runnable() {@Overridepublic void run() {count1.add();}},"t1");t1.start();//让主线程休眠1000毫秒,确保t1一定启动成功了try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}Thread t2=new Thread(new Runnable() {@Overridepublic void run() {count1.add();}},"t2");t2.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//中断t2t2.interrupt();}
}

      图解:

      

但是,一运行程序,发现出现了下面的问题:

  

出现第一个问题的原因我们找到了,那出现第二个问题,,锁状态异常又是什么原因呢? 

回到add()方法当中:

 这就是为什么thread2在被唤醒之后,触发锁状态异常的原因。


经典面试问题: 

ReentrantLock和synchronized有什么不同

使用方法上面的区别

①ReentrantLock可以提供公平+非公平两种特性

     当ReentrantLock构造方法中指定了参数为true的时候,这个锁被确定为公平锁。

      而synchronized无法提供公平锁的特性


 ②ReentrantLock的加锁、解锁操作都是需要手动进行,

       而synchronized的话可以进行自动的加锁、解锁操作。

       synchronized可以有效避免加锁之后忘记解锁的情况。

     当代码执行到synchronized修饰的代码块的时候,如果在同步代码块内部发生了异常,没有及时处理的话,会提前退出并且让线程释放锁。

      而ReentrantLock无法做到立刻解锁,因此,unLock()的解锁操作一定要在finally代码块当中,避免加锁之后忘记解锁的情况。 


③synchronized无法提供lock.tryLock()这样的尝试获取锁的特性,而ReentrantLock可以提供。

        线程如果在指定的时间之内无法获取到锁,或者锁已经被占用了,那么lock.tryLock()可以有效减少线程阻塞等待的情况,或者减少阻塞等待的时间。

       而synchronized只会让无法获取到锁的线程"死等"。直到获取到锁的线程释放锁


④ReentrantLock可以提供中断式加锁。 

        ④ReentrantLock在调用lock.lockInterruptibly()时候,可以让获取不到锁,进入阻塞等待的线程被提前"唤醒",但是synchronized不可以。具体的操作已经在上面解释了。

  下面,给一个场景,验证可中断式加锁。

public static void main(String[] args) throws InterruptedException {ReentrantLock lock=new ReentrantLock(true);Thread thread=new Thread(new Runnable() {@Overridepublic void run() {//让thread获取到锁lock.lock();System.out.println("thread获取到了锁");//不提供解锁的操作,一直让thread占有这把锁}});thread.start();//确保thread已经启动,并且持有锁了Thread.sleep(1000);Thread thread1=new Thread(new Runnable() {@Overridepublic void run() {//不让thread1获取到锁,同时thread1是"可中断式"加锁try {lock.lockInterruptibly();} catch (InterruptedException e) {e.printStackTrace();System.out.println("thread1的阻塞等待被中断了");}}});thread1.start();//main线程尝试唤醒thread1thread1.interrupt();}

   运行程序,可以看到:thread1的阻塞被中断了 


 ⑤ReentrantLock借助Condition类:可以指定唤醒线程

      指定了可以notify()的线程。

      类比于synchronized,如果多个线程因为获取不到锁进入了WAITING或者TIME_WAITING状态。那么,在notify()的时候,只可以随机唤醒一个正在WAITING或者TIME_WAITING的线程。

       但是ReentrantLock借助Condition接口,可以指定唤醒线程


 锁的实现方式上面的区别

     提供的级别不一样:

       ReentrantLock是Java当中的一个具体的,是在API级别提供的锁,

       而synchronized是Java当中提供的一个关键字,是JVM级别提供的锁


       原理不一样: 

       synchronized加锁的过程,涉及了锁升级的过程。

       从无锁->偏向锁->轻量级锁->重量级锁;并且还可能涉及锁粗化、锁消除。

       在下面这一篇文章当中已经提到了:
(2条消息) 【JavaEE多线程】synchronized原理篇_革凡成圣211的博客-CSDN博客https://blog.csdn.net/weixin_56738054/article/details/128826633?spm=1001.2014.3001.5502      但是,ReentrantLock是基于AQS来实现的。

       在这一篇文章当中,我们也提到了什么是AQS,它的核心就是两个属性。一个是内部封装的队列。用来保存获取不到锁的线程。另外一个是state属性,用来标记是否可以获取锁。
(2条消息) Java当中的AQS_革凡成圣211的博客-CSDN博客https://blog.csdn.net/weixin_56738054/article/details/128664083?spm=1001.2014.3001.5502


      ReentrantLock的原理 

       ReentrantLock在初始化的时候,就提供了公平和非公平的两种方式。

        

       在ReentrantLock内部封装了两个静态内部类,一个是FairSync,另外一个是NofairSymc

       分别提供了公平的加锁方式和不公平的加锁方式。

        这两个类都继承于ReentrantLock内部的一个Syn,然后这个Syn又继承于AQS。

          当有线程调用lock方法的时候:

          如果线程获取到锁了,那么就会通过CAS的方式把AQS内部的state设置成为1

          


       这个时候,当前线程就获取到锁了。

        可以看到,只有首部的节点(head节点封装的线程)可以获取到锁。

       其他线程都会加入到这一个阻塞队列当中。

        如果是公平锁的话,当head节点释放锁之后,会优先唤醒head.next这一个节点对应的线程。令head=head.nxet,让下一个节点对应的线程获取到锁。


        如果是非公平锁的话,会让之前head节点之后的节点对应的线程一起采用CAS的方式获取锁。       

     

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/36538.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenAI最新官方ChatGPT聊天插件接口《智能聊天插件引言》全网最详细中英文实用指南和教程,助你零基础快速轻松掌握全新技术(一)(附源码)

Chat Plugins Limited Alpha 聊天插件 前言IntroductionPlugin flow 插件流其它资料下载 Learn how to build a plugin that allows ChatGPT to intelligently call your API. 了解如何构建允许ChatGPT智能调用API的插件。 前言 在现代的软件开发环境中,使用第三方…

Pycharm快速入门(10) — 插件管理

1、插件安装 File | Settings | Plugins | Marketplace 搜索插件点击Install安装 2、插件卸载 File | Settings | Plugins | Installed 选择需要卸载的插件,点击Uninstall。 3、推荐插件 (1)、Chinese ​(Simplified)​ Language Pack &am…

chatgpt赋能python:Python编程的好玩之处:用简单的代码创造奇妙的世界

Python编程的好玩之处:用简单的代码创造奇妙的世界 如果你喜欢写代码,那么Python是一个不错的选择。Python语言设计简单,易学易用,同时还拥有丰富的生态系统,支持许多强大的第三方库和框架,可以使你轻松地…

chatgpt赋能python:Python图片拼图的好处和应用

Python图片拼图的好处和应用 Python是一种高级编程语言,已经被广泛应用于数据科学、网络编程、机器学习等领域。其中,Python的图像处理领域也越来越受关注。在本文中,我们将介绍如何使用Python创建图片拼图,并讨论它的好处和应用…

midjourney教程:如何快速生成个性化Logo设计

midjourney是一款基于人工智能技术的Logo设计工具,它可以帮助用户快速生成个性化的Logo设计,而无需具备专业的设计技能。下面将为大家介绍midjourney的使用方法,以帮助大家轻松生成符合自己需求的Logo设计。 第一步:登录midjourn…

chatgpt赋能python:Python添加图片背景的方法

Python添加图片背景的方法 简介 Python是一种开源的高级编程语言,广泛应用于各个行业中,包括图像处理。添加图片背景是图像处理中的常见需求,通过Python可以很方便地实现。 本篇文章将介绍如何使用Python来给图片添加背景,让您…

chatgpt赋能python:Python怎么做图形

Python怎么做图形 在数据可视化和图像处理方面,Python已经成为了最受欢迎的编程语言之一。Python的图形库使得创建各种图形和图表、可视化工具和图像处理应用程序变得容易而简单。 在本文中,我们将会介绍一些最受欢迎的Python图形库,以帮助…

程序员晒追女神聊天截图,坦言第一次没经验,网友直呼凭实力单身

前段时间网络上一名程序员晒出了自己与女神之间的聊天记录的对话截图,通过截图中我们可以看出,应该是这位程序员在追求这位女神,但是短短的十几分钟几条聊天记录,却以女神不再愿意搭理程序员结束,对于这样的结局&#…

程序员给女友4千生活费,收到女友错发信息后分手,神对话!

如何平衡好亲情爱情的关系,是一门学问,有的人就希望自己的另一半过好他们自己的小日子,不要对家里的事情过多的付出,但有人觉得自己父母养大自己不容易,能有能力的话,不光孝敬爹妈,还会帮衬家里…

程序员就是这样聊天把女朋友聊没的

身为程序员 都想当然的认为 身为一个优秀的程序员 我怎么可能会没女票 这不科学啊 工资高,话少 有一天看到了 某个程序员的聊天记录 有女孩主动搭讪 这么绝好的机会 然后你竟然说忙 说忙 忙... 主动找你搭讪 你还不抓紧机会约约约 如果改成&#xff1a…

被程序员的相亲规划整不会了......

近日,北京一程序员将自己7天7场相亲行程规划表发到论坛分享,感叹到:真不是凡尔赛,相亲比上班还难,引来大量网友围观。 相亲也有规划表? 据介绍,该程序员今年刚好30岁,自己平时加班多…

程序员吐槽女朋友狮子大开口

本文转载自程序员八卦 一个程序员发帖吐槽自己的潮汕女朋友,开口要彩礼18万8,楼主在网上查了一下,一般潮汕彩礼是3万到8万,难道外地人要多给一点吗?而且女朋友还一定要楼主父母出彩礼,不能楼主自己出&…

最最普通程序员,如何利用工资攒够彩礼,成为人生赢家

今天我们不讲如何提升你的专业技能去涨工资,不讲面试技巧如何跳槽涨工资,不讲如何干兼职赚人生第一桶金,就讲一个最最普通的程序员,如何在工作几年后,可以攒够彩礼钱,婚礼酒席钱,在自己人生大事…

如何做好小红书?从找好定位开始,这篇文章告诉你

近年来小红书随着用户体量壮大和平台多元化发展,用户的兴趣点,早已从美妆独大变为渗透生活领域的各个方面。与以往相比,大家对小红书的认知也逐渐在发生变化。 如果说去年还有不少商家还经常问我们“为什么要做小红书?”。那么&am…

测试听力口语软件,上、英语系学姐最全整理的34个英语学习App 针对听力、口语、阅读...

英语的重要性不用我多说啦~日常生活、工作,不擅长英语真的会失去很多机会和乐趣 作为英语系学姐今天就给大家总结了一些学习英语的app 有需要就马住,慢慢学习! 听力 听力学习,都非常好用 -朗易思听 页面超精美,资源也很…

小红书怎么运营好?分享小红书的一些经验让你少走弯路

每次讲小红书运营,我都尽量把一个问题拆的特别细,揉碎了讲,说实话挺不容易的。之前也发过,这次分享又是小红书,没办法,小红书的流量非常大,而且粉丝精准度也很不错。 分享的这些都是经验&#…

chatgpt赋能python:Python手机密码解锁-打开手机的一条捷径

Python手机密码解锁-打开手机的一条捷径 我们都遇到过忘记手机密码的经历。不管是因为长时间不用手机导致遗忘,还是输入错误太多次,导致手机被锁定,让我们感到非常困扰和苦恼。虽然我们可以通过向手机厂商寻求帮助或找专业维修技术人员来解锁…

16个小的UI设计规则却能产生巨大的影响

微信搜索 【大迁世界】, 我会第一时间和你分享前端行业趋势,学习途径等等。 本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。 快来免费体验ChatGpt plus版本的,我们出的钱 体验地…

每个前端开发者都应知道的25个实用网站

微信搜索 【大迁世界】, 我会第一时间和你分享前端行业趋势,学习途径等等。 本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。 快来免费体验ChatGpt plus版本的,我们出的钱 体验地…

强大的人工智能聊天机器人 ChatGPT 是如何工作的,看完这篇文章你就明白了

公众号关注 「奇妙的 Linux 世界」 设为「星标」,每天带你玩转 Linux ! 作者:Stephen Wolfram,发表于 2023年2月14日 翻译:DeepL,校对:李笑来 提示:建议收藏后阅读,本文包…