当前位置: 首页 > news >正文

java可重入锁(ReentrantLock)的实现原理

为什么80%的码农都做不了架构师?>>>   hot3.png

前言

相信学过java的人都知道 synchronized 这个关键词,也知道它用于控制多线程对并发资源的安全访问,兴许,你还用过Lock相关的功能,但你可能从来没有想过java中的锁底层的机制是怎么实现的。如果真是这样,而且你有兴趣了解,今天我将带领你轻松的学习下java中非常重要,也非常基础的可重入锁-ReentrantLock的实现机制。

听故事把知识掌握了

在一个村子里面,有一口井水,水质非常的好,村民们都想打井里的水。这井只有一口,村里的人那么多,所以得出个打水的规则才行。村长绞尽脑汁,最终想出了一个比较合理的方案,咱们来仔细的看看聪明的村长大人的智慧。

井边安排一个看井人,维护打水的秩序。

打水时,以家庭为单位,哪个家庭任何人先到井边,就可以先打水,而且如果一个家庭占到了打水权,其家人这时候过来打水不用排队。而那些没有抢占到打水权的人,一个一个挨着在井边排成一队,先到的排在前面。打水示意图如下 :

打水示意图

是不是感觉很和谐,如果打水的人打完了,他会跟看井人报告,看井人会让第二个人接着打水。这样大家总都能够打到水。是不是看起来挺公平的,先到的人先打水,当然不是绝对公平的,自己看看下面这个场景 :

同家人一起打水

看着,一个有娃的父亲正在打水,他的娃也到井边了,所以女凭父贵直接排到最前面打水,羡煞旁人了。 
以上这个故事模型就是所谓的公平锁模型,当一个人想到井边打水,而现在打水的人又不是自家人,这时候就得乖乖在队列后面排队。

事情总不是那么一帆风顺的,总会有些人想走捷径,话说看井人年纪大了,有时候,眼力不是很好,这时候,人们开始打起了新主意。新来打水的人,他们看到有人排队打水的时候,他们不会那么乖巧的就排到最后面去排队,反之,他们会看看现在有没有人正在打水,如果有人在打水,没辄了,只好排到队列最后面,但如果这时候前面打水的人刚刚打完水,正在交接中,排在队头的人还没有完成交接工作,这时候,新来的人可以尝试抢打水权,如果抢到了,呵呵,其他人也只能睁一只眼闭一只眼,因为大家都默认这个规则了。这就是所谓的非公平锁模型。新来的人不一定总得乖乖排队,这也就造成了原来队列中排队的人可能要等很久很久。

java可重入锁-ReentrantLock实现细节

ReentrantLock支持两种获取锁的方式,一种是公平模型,一种是非公平模型。在继续之前,咱们先把故事元素转换为程序元素。

元素转换 
 

咱们先来说说公平锁模型:

初始化时, state=0,表示无人抢占了打水权。这时候,村民A来打水(A线程请求锁),占了打水权,把state+1,如下所示:

线程A获取锁

线程A取得了锁,把 state原子性+1,这时候state被改为1,A线程继续执行其他任务,然后来了村民B也想打水(线程B请求锁),线程B无法获取锁,生成节点进行排队,如下图所示:

线程B等待

初始化的时候,会生成一个空的头节点,然后才是B线程节点,这时候,如果线程A又请求锁,是否需要排队?答案当然是否定的,否则就直接死锁了。当A再次请求锁,就相当于是打水期间,同一家人也来打水了,是有特权的,这时候的状态如下图所示:

可重入锁获取

到了这里,相信大家应该明白了什么是可重入锁了吧。就是一个线程在获取了锁之后,再次去获取了同一个锁,这时候仅仅是把状态值进行累加。如果线程A释放了一次锁,就成这样了:

线程A释放一次锁

仅仅是把状态值减了,只有线程A把此锁全部释放了,状态值减到0了,其他线程才有机会获取锁。当A把锁完全释放后,state恢复为0,然后会通知队列唤醒B线程节点,使B可以再次竞争锁。当然,如果B线程后面还有C线程,C线程继续休眠,除非B执行完了,通知了C线程。注意,当一个线程节点被唤醒然后取得了锁,对应节点会从队列中删除。 
 

非公平锁模型

如果你已经明白了前面讲的公平锁模型,那么非公平锁模型也就非常容易理解了。当线程A执行完之后,要唤醒线程B是需要时间的,而且线程B醒来后还要再次竞争锁,所以如果在切换过程当中,来了一个线程C,那么线程C是有可能获取到锁的,如果C获取到了锁,B就只能继续乖乖休眠了。这里就不再画图说明了。 
 

其它知识点

java5中添加了一个并发包, java.util.concurrent,里面提供了各种并发的工具类,通过此工具包,可以在java当中实现功能非常强大的多线程并发操作。对于每个java攻城狮,我觉得非常有必要了解这个包的功能。虽然做不到一步到位,但慢慢虚心学习,沉下心来,总能慢慢领悟到java多线程编程的精华。 
 

结束语

可重入锁的实现会涉及到CAS,AQS,java内存可见性(volatile)等知识,为了避免大家直接被代码搞晕,故而想以最简单的方式把可重入锁进行抽象,讲明白其中的实现原理,这样看起源码也有个借鉴的思路,希望本篇能够帮助到你们。

转载于:https://my.oschina.net/cccyb/blog/1589494

相关文章:

  • React Native声明属性和属性确认
  • JavaScript深入之词法作用域和动态作用域
  • 竞赛回忆录
  • 简单团队-爬取豆瓣电影TOP250-需求分析
  • JS实现简单的MVC模式开发小游戏
  • 虚拟就Ubuntu 14.0.4 安装配置jenkins
  • 大数据学习(2)HDFS文件管理
  • Mac 10.12安装截图工具Jietu
  • Centos 搭建Mysql-Proxy 读写分离
  • 【小白学云计算】10分钟搭建一台web服务器!(就是快!)
  • [docker]docker网络-直接路由模式
  • SQL 简介
  • Jenkins 集成 Sonar
  • 初识HTTP
  • jfinal-admin 后台框架永久开源
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • isset在php5.6-和php7.0+的一些差异
  • Iterator 和 for...of 循环
  • JavaScript 事件——“事件类型”中“HTML5事件”的注意要点
  • Java小白进阶笔记(3)-初级面向对象
  • Joomla 2.x, 3.x useful code cheatsheet
  • jquery ajax学习笔记
  • Mithril.js 入门介绍
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • Vue.js-Day01
  • 彻底搞懂浏览器Event-loop
  • 快速构建spring-cloud+sleuth+rabbit+ zipkin+es+kibana+grafana日志跟踪平台
  • 微信小程序填坑清单
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • !!Dom4j 学习笔记
  • !$boo在php中什么意思,php前戏
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (补)B+树一些思想
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (附源码)springboot金融新闻信息服务系统 毕业设计651450
  • (附源码)ssm经济信息门户网站 毕业设计 141634
  • (转)Unity3DUnity3D在android下调试
  • (轉貼) 寄發紅帖基本原則(教育部禮儀司頒布) (雜項)
  • .net 受管制代码
  • .NET 应用启用与禁用自动生成绑定重定向 (bindingRedirect),解决不同版本 dll 的依赖问题
  • .NET/ASP.NETMVC 大型站点架构设计—迁移Model元数据设置项(自定义元数据提供程序)...
  • .NET/C# 使用 SpanT 为字符串处理提升性能
  • .NetCore 如何动态路由
  • @angular/cli项目构建--http(2)
  • @Autowired注解的实现原理
  • @SuppressWarnings(unchecked)代码的作用
  • [].slice.call()将类数组转化为真正的数组
  • [2017][note]基于空间交叉相位调制的两个连续波在few layer铋Bi中的全光switch——
  • [Android Pro] listView和GridView的item设置的高度和宽度不起作用
  • [ASP.NET MVC]如何定制Numeric属性/字段验证消息