Java 并发底层知识,锁获取超时机制知多少?

Wesley13
• 阅读 736

当我们在使用Java进行网络编程时经常会遇到很多超时的概念,比如一个浏览器请求过程就可能会产生很多超时的地方,当我们在浏览器发起一个请求后,网络socket读写可能会超时,web服务器响应可能会超时,数据库查询可能会超时。而对于Java并发来说,与超时相关的内容主要是线程等待超时和获取锁超时,比如调用Object.wait(long)就会使线程进入等待状并在指定时间后等待超时。

Java 并发底层知识,锁获取超时机制知多少?

此篇主要讲解Java内置锁的获取操作的超时机制。当大量线程对某一锁竞争时可能导致某些线程在很长一段时间都获取不了锁,在某些场景下可能希望如果线程在一段时间内不能成功获取锁就取消对该锁的等待以提高性能,这时就需要用到超时机制。

Java 并发底层知识,锁获取超时机制知多少?

Synchronized 不支持超时

我们先看Java从语法层提供的并发锁——synchronized关键词,synchronized对我们来说是相当熟悉的了,它是Java内置的锁方案。在Java的世界,每个对象都关联着一个内置锁,当线程要访问被synchronized修饰的对象时都必须先获得其对应的锁才能继续访问,否则将一直等待直到该锁被其它线程所释放。普通对象和对象的方法都关联有对应的内置锁,所以它们都可以被synchronized修饰。

Java 并发底层知识,锁获取超时机制知多少?

虽然synchronized使用很方便,但其存在一个缺点,那就是锁获取操作不支持超时机制。在并发的情况下,多个线程会去竞争被synchronized所修饰对应的锁对象,可能存在某个线程一直获取不到锁而一直处于阻塞等待状态。而这个处于阻塞状态的线程唯一能做的就是一直等待,我们没有办法设置一个等待超时时间。以下面的代码为例,线程一会先成功获取锁,在输出“Thread1 gets the lock”后进入睡眠,睡眠的时间很长。线程二较晚启动,它尝试获取锁,但该锁已被线程一所持有,所以线程一将永远获取不到锁而一直等待。

Java 并发底层知识,锁获取超时机制知多少?

AQS 同步器超时机制

在JDK1.5之前还没有JUC工具,当时的并发控制只能通过上述的synchronized关键词实现锁,但它对超时取消的控制力不从心。JDK1.5开始引入的JUC工具则完美地解决了此问题,主要是因为AQS同步器提供了锁获取超时的支持。我们知道AQS同步器使用了队列的结构来处理等待的线程,AQS获取锁的超时机制大致如下图所示。首先多个线程竞争锁,因为锁已被其它线程持有,所以通过自旋的CAS操作将各自线程添加到队尾。其次是在线程添加到队列后,每个线程节点都各自轮询前一节点看是否轮到自己获取锁。假如这里线程2设置了超时机制,且线程2在超时时间内都获取不到锁,则该线程对应的节点将被取消。最终线程2因为获取锁超时而被取消。

Java 并发底层知识,锁获取超时机制知多少?

超时实现逻辑

为了更精确地保证时间间隔的准确性,实现时使用了更为精确的System.nanoTime()方法,它能精确到纳秒级别。总体而言,超时机制的思想就是先计算deadline时间,然后在不断进行锁检查操作中计算是否已经到deadline时间,如果已到deadline时间则取消队列中的该节点并跳出循环。

AQS的超时控制有两点必须要注意:

  • 一是超时时间包括了竞争入队的时间,如果竞  争入队就把超时时间消耗完的话则直接当作超时处理;

  • 另一个是关于spinForTimeoutThreshold变量阀值,它是决定使用自旋方式消耗时间还是使用系统阻塞方式消耗时间的分割线。

JUC工具包作者通过测试将默认值设置为1000ns,即如果在成功插入等待队列后剩余时间大于1000ns则调用系统底层阻塞。否则不调用系统底层阻塞,取而代之的是仅仅让其在Java层不断循环消耗时间,这属于性能优化的措施。

Java 并发底层知识,锁获取超时机制知多少?

总结

Java内置的synchronized关键词虽然提供了并发锁功能,但它却存在不支持超时的缺点。而AQS同步器则在获取锁的过程中提供了超时机制,同时我们深入分析了AQS获取锁超时的具体实现原理。获取锁超时的支持让Java在并发方面提供了更完善的机制,能满足开发者更多的并发策略需求。

Java 并发底层知识,锁获取超时机制知多少?

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
九鹤 九鹤
3年前
并发编程的基础概念
什么是线程?什么是进程?java可以开启线程吗?不能因为Java无法直接操硬件,他是运行在虚拟机上面的,什么是并发?什么是并行?并发就是多个线程去操作一个资源。并行是多个线程同时行,但是操作的资源不是同一个。线程的六个状态new(诞生)runnable(运行)Blocked(阻塞)waiiiing(等待)Tiemwaiing(超时等待)
Stella981 Stella981
3年前
Nginx + lua +[memcached,redis]
精品案例1、Nginxluamemcached,redis实现网站灰度发布2、分库分表/基于Leaf组件实现的全球唯一ID(非UUID)3、Redis独立数据监控,实现订单超时操作/MQ死信操作SelectPollEpollReactor模型4、分布式任务调试Quartz应用
Stella981 Stella981
3年前
Hystrix 服务的隔离策略对比,信号量与线程池隔离的差异
hytrix支持线程池隔离和信号量隔离信号量隔离适应非网络请求,因为是同步的请求,无法支持超时,只能依靠协议本身线程池隔离,即,每个实例都增加个线程池进行隔离先给个总结对比:隔离方式是否支持超时是否支持熔断隔离原理
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
C# 给某个方法设定执行超时时间
参考:https://blog.csdn.net/educast/article/details/7430932进群:!(https://filescdn.cnblogs.com/files/luofuxian/jqerweima.bmp)  由于某些方法可能产生阻止线程继续执行,需要定义防止超时,且可以得到是否超时,超时可执行某些操作;主
线上SQL超时场景分析-MySQL超时之间隙锁 | 京东物流技术团队
前言之前遇到过一个由MySQL间隙锁引发线上sql执行超时的场景,记录一下。背景说明分布式事务消息表:业务上使用消息表的方式,依赖本地事务,实现了一套分布式事务方案消息表名:mqmessages数据量:3000多万索引:createtime和statuss