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

JAVA多线程机制解析-volatilesynchronized

java代码先编译成字节码,字节码最后编译成cpu指令,因此Java的多线程实现最终依赖于jvm和cpu的实现

synchronized和volatile

我们先来讨论一下volatile关键字的作用以及实现机制,每个线程看到的用volatile修饰的变量的值都是最新的,更深入的解释就涉及到Java的内存模型了,我们知道Java将内存分为主内存和线程私有内存,所有的全局变量都在主内存中,每个线程使用变量时都会从主内存中读取变量,然后放到各自线程的私有内存中,这样线程使用变量时就不用每次都去读取主内存了,当然这也产生了一个问题,如果线程修改了变量值,但是修改的值没有及时地同步到主内存中,那么其他线程看到的变量值仍然是未修改之前的值,这就产生了并发问题,而当这个变量用volatile修饰后,每次线程都会从主内存中读取变量值,也就是说抛弃了线程私有内存中的变量值,而线程每次修改变量后,就会将修改后的值同步到主内存中去。

理论上volatile就是这么实现滴,但是volatile最终会被编译成机器指令,所以volatile这种机制也需要相关的机器指令支持,学习过计算机组成原理的同学们都知道计算机在cpu和主内存直接有一个cache缓存,是不是和Java模型很类似,当然还不一样,其实volatile最终使用了cpu缓存一致性也就是说将缓存中的内容立刻更新到主内存中去,同时将其他缓存中的值置为无效。如果大家有探索精神可以看看看看volatile被编程为的汇编代码,就会发现volatile是用Lock信号实现地。

下面我们再来说说synchronized关键字,学习过Java的同学应该都知道这个关键字是用来实现多线程同步的,synchronized的具体用法这里就不介绍了,我们来聊下这个关键字的具体实现,看过Java并发的同学都会发现synchronized被称为重量级锁,怎么理解这个重量级的概念那?反正我的理解是加锁解锁耗费地时间多,导致并发度比较低呗,但是随着JDK版本的升级,synchronized的性能和并发库中Lock的性能基本持平。好了言归正传,synchronized的具体实现不知道同学们了解多少,我想大部分人应该能说出synchronized由一个同步队列和对象监视器实现,线程进入同步块时,先获取监视器如果成功就进入同步块,如果不成功就进入同步队列等待另外一个线程从同步块退出,没错这就是synchronized的实现,如果你只知道这些说明你了解的还不够深入,因为你还需要知道偏向锁、轻量级锁和synchronized的关系,在这里我就抛砖引玉先说说我自己的理解吧,我们都知道一个Java对象有三部分组成,对象头,实体部分,对齐填充部分,这个对象头就是实现synchronized的关键,在比较老的JDK版本中,一个线程进入同步代码块时就要获取互斥量,不管有没有其他线程,改进后的synchronized,线程一般先获取偏向锁,如果有竞争就膨胀为轻量级锁或者重量级锁,轻量级锁又会膨胀为重量级锁,那么偏向锁、轻量级锁、重量级锁有什么区别那?

偏向锁:

对象头中设置偏向锁标记,同时将当前线程的线程id保存在对象头和栈的锁记录空间中;

轻量级锁

将对象头的内容复制到当前线程栈的锁记录空间中,同时将对象头设置为指向锁记录空间的指针;

重量级锁
将对象头的内容复制到当前线程的锁记录空间中,同时将对象头设置为指向互斥量的指针;

当一个线程进入同步快时,先通过cas设置对象头为偏向锁,若设置成功则说明线程获取锁,若设置不成功,说明锁存在竞争,持有偏向锁的线程就要释放偏向锁,偏向锁膨胀为轻量级锁或重量级锁。
当一个线程持有轻量级锁,另外一个线程尝试获取锁,获取失败,则另外一个线程会自旋等待,若等待一段时间仍未获取锁,则线程进入等待,持有轻量级锁的线程就会在安全点处释放轻量级锁,轻量级锁也会膨胀为重量级锁。
当一个线程持有重量级锁时,另外一个线程就会被直接踢到同步队列中等待。

(安全点是jvm的一些特殊位置,在这个位置上所有的线程都会暂停工作,一般在安全点处进行垃圾回收,还有一个概念是安全区域,安全区域是指在一块代码内引用关系不会发生变化,这个代码的任何位置进行垃圾回收都是可以的)

相关文章:

  • 基于hi-nginx的web开发(python篇)——cookie和会话管理
  • hostPath Volume - 每天5分钟玩转 Docker 容器技术(148)
  • 使用vigil 监控微服务系统包含可视化界面
  • Centos查看端口占用情况和开启端口命令
  • 分布式计算框架MapReduce
  • php新特性:trait 关键字使用
  • BZOJ2938:[POI2000]病毒(AC自动机)
  • MaxCompute访问TableStore(OTS) 数据
  • 并发之痛 Thread,Goroutine,Actor
  • qca wlan wifi modules解析二
  • 结合 Laravel 初步学习 GraphQL
  • 实验三 类与对象(zxt)
  • 翻译:DECLARE HANDLER语句(已提交到MariaDB官方手册)
  • 窥探Node.js里的Stream
  • 给mybatis添加自动建表,自动加字段的功能
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • [js高手之路]搞清楚面向对象,必须要理解对象在创建过程中的内存表示
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • ES6系列(二)变量的解构赋值
  • React Transition Group -- Transition 组件
  • vue 个人积累(使用工具,组件)
  • 记一次用 NodeJs 实现模拟登录的思路
  • 来,膜拜下android roadmap,强大的执行力
  • 你不可错过的前端面试题(一)
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 思否第一天
  • 学习Vue.js的五个小例子
  • 与 ConTeXt MkIV 官方文档的接驳
  • 原生js练习题---第五课
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • #includecmath
  • (2)(2.10) LTM telemetry
  • (day 2)JavaScript学习笔记(基础之变量、常量和注释)
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (附源码)springboot 基于HTML5的个人网页的网站设计与实现 毕业设计 031623
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (一)为什么要选择C++
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • .NET Framework .NET Core与 .NET 的区别
  • .net framwork4.6操作MySQL报错Character set ‘utf8mb3‘ is not supported 解决方法
  • .NET 表达式计算:Expression Evaluator
  • .Net 代码性能 - (1)
  • .NetCore 如何动态路由
  • .NetCore实践篇:分布式监控Zipkin持久化之殇
  • @selector(..)警告提示
  • [ Linux 长征路第二篇] 基本指令head,tail,date,cal,find,grep,zip,tar,bc,unname
  • [C#]OpenCvSharp结合yolov8-face实现L2CS-Net眼睛注视方向估计或者人脸朝向估计
  • [codeforces]Levko and Permutation
  • [javaSE] 看知乎学习工厂模式
  • [Kubernetes]9. K8s ingress讲解借助ingress配置http,https访问k8s集群应用
  • [leetcode] 103. 二叉树的锯齿形层次遍历