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

Synchronized原理分析

并发的三大特性

        并发三大特性:原子性、可见性、有序性。之前我们说过volatile,它可以保证可见性和有序性,但是不能保证原子性。具体详见 https://blog.csdn.net/u010096526/article/details/133817363,这次说的Synchronized确是能保证这3种特性的。

原子性

        我们都知道原子性的定义是:要么都执行,要么都不执行。类似i++和i+=1这种,都不是原子性的。因为它拆分了数据的读取、计算、赋值。而synchronized修饰的类或对象的操作都是原子性的,因为只要获取到锁,直到锁释放,这中间的过程无法被打断。所以保证了原子性。

可见性

        volatile的可见性是通过locks前缀指令和内存屏障来保证的。synchronized修饰类或对象时,一个线程如果想访问这个该类或对象,必须要先获取锁,而这个锁的状态对其他线程都是可见的,并且会在锁释放之前会将对变量的修改刷回主存,这就是保证了可见性。

有序性

        会保证代码的执行顺序,防止指令重排。volatile是通过内存屏障。而synchronize本来就是控制只能单线程访问,所以单线程肯定是有序的。

原理     

           

        其实synchronized底层的原理,是跟jvm指令和monitor有关系的。在底层编译后的jvm指令中,会有monitorenter和monitorexit两个指令。

        每个对象都有一个关联的monitor,比如一个对象实例就有一个monitor,一个类的Class对象也有一个monitor,如果要对这个对象加锁,那么必须获取这个对象关联的monitor的lock锁。monitor里面有一个计数器,从0开始的。如果一个线程要获取monitor的锁,就看看他的计数器是不是0,如果是0的话,那么说明没人获取锁,他就可以获取锁了,然后对计数器加1。

        Synchronized可重入锁也是根据monitor的计数器。

        如果一个线程第一次synchronized那里,获取到了myObject对象的monitor的锁,计数器加1,然后第二次synchronized那里,会再次获取myObject对象的monitor的锁,这个就是重入加锁了,然后计数器会再次加1,变成2。

        这个时候,其他的线程在第一次synchronized那里,会发现说myObject对象的monitor锁的计数器是大于0的,意味着被别人加锁了,然后此时线程就会进入block阻塞状态,什么都干不了,就是等着获取锁。

        接着如果出了synchronized修饰的代码片段的范围,就会有一个monitorexit的指令,在底层。此时获取锁的线程就会对那个对象的monitor的计数器减1,如果有多次重入加锁就会对应多次减1,直到最后,计数器是0。

        然后后面block住阻塞的线程,会再次尝试获取锁,但是只有一个线程可以获取到锁。


http://www.kler.cn/a/153722.html

相关文章:

  • syslog定期rotate和限制其size的配置方法ubuntu上
  • 如何使用torchrun启动单机多卡DDP并行训练
  • opencv2.4 android编译
  • 内存免杀--
  • ubuntu18编译Android8的Failed to contact Jack server问题
  • 激光SLAM:Faster-Lio 算法编译与测试
  • python+pytest接口自动化(6)-请求参数格式的确定
  • LeetCode56. 合并区间
  • FL Studio2024重磅更新 带你了解FL21.2最新版本功能
  • C语言 柔性数组
  • RK3568笔记六:基于Yolov8的训练及部署
  • LCR 047. 二叉树剪枝 和 leetCode 1110. 删点成林 + 递归 + 图解
  • CentOS 系列:CentOS 7 使用 virt-install + vnc 图形界面/非图形界面 创建虚拟机
  • clickhouse的向量化执行
  • 熬夜会秃头——Beta冲刺总结随笔
  • 39.从0到上线三天搭建个人网站(第三天)
  • Node.js入门指南(完结)
  • 【Apache Doris】Manager极致丝滑地运维管理
  • IO流--12--Java lO 设计模式
  • Kibana使用指南