[Java并发系列] Java并发机制的底层实现

在Java并发实现的机制中,大部分的容器和框架都是依赖于volatile/synchronized/原子操作实现的,了解底层的并发机制,对于并发编程会带来很多帮助

1. synchronized的应用

synchronized在多线程并发编程中已经是一个元老级的存在,通常被称作是重量级锁。既然是常用的一种锁,那么就需要对它的底层实现有深入的了解。

1. synchronized的实现原理

当一个线程在访问同步代码块时,就必须要先获取该代码块中对象的锁,退出或者抛出异常时,就必须要释放锁。synchronized的同步实现,是JVM基于进入和退出同步对象的Monitor对象来实现方法同步和代码块同步的。 每个Monitor对象都有与之关联的monitor,当且仅当monitor被持有后,它才会处于锁定状态。同步代码是使用monitorenter和monitorexit指令实现的。monitorenter指令时在代码编译后插入到同步代码块的开始位置,monitorexit指令则是插入到方法的结束和异常处。当线程执行到monitorenter时,会尝试去获取对象的monitor的所有权,即尝试获取对象的锁。

2. synchronized的使用形式及意义
  • 修饰普通方法:锁是当前实例对象
  • 修饰静态方法:锁是当前类的Class对象
  • 修饰代码块:锁是synchronized括号里面配置的对象
3. 锁的升级

锁共有四种状态:无锁状态->偏向锁状态->轻量级锁状态->重量级锁状态

偏向锁:当线程访问同步块并获取锁时,会在对象头和栈针中的锁记录中存储锁偏向的ID,以后该线程在进入和退出同步块时就不需要在使用CAS操作来进行加锁和解锁操作,而仅仅需要测试一下对象头中的Mark Word字段中存储的线程ID是否是当前线程即可。如果是,那么表示获取锁成功;如果不是,则需要检查对象头中偏向锁的字段是否设置为了1(表示当前锁是偏向锁),如果没有设置,则需要使用CAS来竞争获取锁,如果已经设置了,则尝试使用CAS将对象头的偏向锁ID指向当前线程。 轻量级锁;

  • 轻量级锁加锁:线程在执行同步块之前,首先会在自己的线程栈中创建一个用于存储锁记录的空间,并将对象中的Mark Word 赋值到锁记录中。然后线程尝试使用CAS操作将对象头中的Mark Word替换为指向锁记录的指针。如果成功,则表示当前线程获取锁,如果失败,则表示其他线程也在竞争锁,当前线程便使用自循的方式来获取锁。
  • 轻量级锁解锁:轻量级锁解锁时,会使用原子CAS操作将锁记录替换会对象头,如果成功,则表示没有竞争发生。如果失败,则表示当前锁存在竞争,锁就会膨胀为重量级锁。
4. 锁的对比

锁类型

优点

缺点

应用场景

重量级锁

线程竞争不使用自旋,不会浪费CPU

线程阻塞,响应时间慢

追求吞吐量,同步块执行的时间长的场景

轻量级锁

竞争的线程不会阻塞,提高程序的响应速度

如果线程长时间得不到锁,那么自旋就会浪费CPU

追求响应时间,同步块执行速度快

偏向锁

加锁和解锁不需要额外的资源消耗

如果线程间存在竞争,会带来额外的锁撤销的消耗

适用于只有一个线程访问同步场景

2. volatile的应用

volatile是轻量级的synchronized,volatile在多线程开发中保证了共享变量的可见性,所谓可见性,指的是当一个线程修改了共享变量之后,对于其他线程来说,能够读到这个修改的值。

1. volatile的定义及实现原理

volatile定义:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁获得这个变量。 实现原理:当对声明了volatile的变量进行写操作时,JVM就会向处理器发送一条带有lock前缀的指令,将这个变量所在的缓存行的数据写回到系统内存中。同时,在多处理器的情况下,需要执行缓存一致性协议,即每个处理器都需要通过嗅探总线上传播的数据来检查自己的缓存是否过期,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置为无效,当处理器需要对这个数据进行操作时,再从系统内存中把数据读取到缓存行中。

2. 实现volatile的两条原则
  • 带有Lock前缀的指令会引起处理器缓存写回到内存;
  • 一个处理器的缓存写回到内存,会导致其他处理器的缓存无效。

3. 原子操作的原理

见文章[并发编程系列]Java中的原子操作类

原文发布于微信公众号 - 瞎说开发那些事(jsj201501)

原文发表时间:2017-11-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏java 成神之路

使用 NIO 实现 echo 服务器

4827
来自专栏pangguoming

Spring Boot集成JasperReports生成PDF文档

由于工作需要,要实现后端根据模板动态填充数据生成PDF文档,通过技术选型,使用Ireport5.6来设计模板,结合JasperReports5.6工具库来调用渲...

1.2K7
来自专栏Golang语言社区

【Golang语言社区】GO1.9 map并发安全测试

var m sync.Map //全局 func maintest() { // 第一个 YongHuomap := make(map[st...

4858
来自专栏C#

DotNet加密方式解析--非对称加密

    新年新气象,也希望新年可以挣大钱。不管今年年底会不会跟去年一样,满怀抱负却又壮志未酬。(不过没事,我已为各位卜上一卦,卦象显示各位都能挣钱...)...

4988
来自专栏杨龙飞前端

scrollto 到指定位置

2554
来自专栏张善友的专栏

Silverlight + Model-View-ViewModel (MVVM)

     早在2005年,John Gossman写了一篇关于Model-View-ViewModel模式的博文,这种模式被他所在的微软的项目组用来创建Expr...

3038
来自专栏张善友的专栏

Miguel de Icaza 细说 Mix 07大会上的Silverlight和DLR

Mono之父Miguel de Icaza 详细报道微软Mix 07大会上的Silverlight和DLR ,上面还谈到了Mono and Silverligh...

2737
来自专栏一个会写诗的程序员的博客

Spring Reactor 项目核心库Reactor Core

Non-Blocking Reactive Streams Foundation for the JVM both implementing a Reactiv...

2232
来自专栏我和未来有约会

Silverlight第三方控件专题

这里我收集整理了目前网上silverlight第三方控件的专题,若果有所遗漏请告知我一下。 名称 简介 截图 telerik 商 RadC...

4075
来自专栏闻道于事

js登录滑动验证,不滑动无法登陆

js的判断这里是根据滑块的位置进行判断,应该是用一个flag判断 <%@ page language="java" contentType="text/html...

7198

扫码关注云+社区