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

Java并发编程:synchronized机制

1. 简介

  synchronizedJava 中最基本的一种同步机制,是 Monitor 机制(监视器机制或管程机制)在 JVM 中的实现,使用 synchronized 时关联的 Java 对象被称为监视器对象, 而被 synchronized 保护的代码被称为同步代码块(或者临界代码区),同一时刻只能有一个线程执行同步代码块,synchronized 有四个特性:

  1. 原子性:被 synchronized 保护的代码块的执行式原子的;
  2. 可见性:在同步代码块中被修改的共享变量会在锁释放之前刷新到主存当中,保证资源变量的可见性;
  3. 有序性:Java允许编译器和处理器对指令进行重排序,但是指令重排并不会影响单线程的顺序,它影响的是多线程并发执行的顺序性,但是被 synchronized 保护的同步代码块,在执行过程中实际上被串行化,同一时刻只允许有一个线程执行同步代码块,因此 synchronized 可以保证同步代码块的有序性;
  4. 可重入性:synchronized 使用的锁是可重入的,当一个线程试图操作一个由其他线程持有的对象锁的临界资源时,将会处于阻塞状态,但当一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入锁,通俗一点讲就是说一个线程拥有了锁仍然还可以重复申请锁。

2. Monitor 机制简介

Monitor 机制主要包含两种机制

  1. 互斥同步机制:由 Monitor 机制同步的临界区,线程之间只能进行互斥访问,每一时刻只有一个线程可以进入临界区;
  2. 等待唤醒机制:又叫条件机制,由于 Monitor 机制采取互斥同步机制,因此,为了防止线程等待其他资源而长时间占有锁,Monitor 机制还包含条件机制,线程可以在某条件未发生时,先挂起阻塞并释放锁给其他线程,等待条件发生时再被唤醒;

实现互斥同步机制需要互斥锁,实现条件机制需要条件对象来提供提供等待通知功能。

3. 监视器对象

  前文介绍了 synchronizedJVM 中的 Monitor 机制,而管程机制需要互斥锁和条件对象,在 JVM 中互斥锁和条件对象的角色都由 Java 对象来扮演,作为互斥锁和条件对象的 JAVA 对象被称为监视器对象,任何 Java 对象都可以作为监视器对象,监视器对象同时承担互斥锁和条件对象的角色,我们看看具体是如何实现的。

Java 对象头

JVM 中,Java 对象包含三个部分:对象头、实例数据、对齐填充,其中对象头一般由 8 个字节 64 个比特位组成,其中前 4 个字节 32 个比特位是标记域(Mark Word),Mark Word 里默认存储对象的 HashCode、分代年龄和锁标记位,其中最后两个比特位就是锁标记位,Mark Word 里面的内容会随着锁标记位的变化而变化。

监视器对象的锁策略

synchronized 管程机制中,监视器对象扮演了锁的角色,在 JDK1.6 之前,监视器的互斥锁只采用了重量级锁策略,而在 JDK1.6 之后,JVM对监视器锁进行了优化,采用混合锁策略并且实现了锁膨胀机制,我们先介绍各种锁策略。

  1. 重量级锁:使用底层操作提供的互斥量来实现锁,比如 linux 操作系统的 mutex,由于重量级锁需要使用底层操作系统提供的资源,因此需要进行系统调用,从而产生用户态到内核态的切换,而且竞争锁失败的线程会被操作系统挂起,等待锁释放;当对象头中的锁标记位为 0b10 时,对象头中 Mark Word 其他比特位的内容变成指向系统互斥量的指针。
  2. 轻量级锁:使用进程内的私有变量 + CAS操作来实现锁,比如锁标记位为 0b00 时,表示使用轻量级锁,轻量级锁通过基于 CAS 操作的同步算法来修改对象头的 Mark Word 域,修改成功,说明当前线程获取锁,如果修改失败,说明发生了竞争,于重量级锁不同,轻量级锁竞争失败后,需要调用方对竞争失败的线程进行同步处理,JVM 采取了自旋重试的策略。
  3. 偏向锁:偏向锁假定将来只有第一个申请锁的线程会使用锁,因此将第一个获取锁的的线程ID通过 CAS 记录在 Mark Word 中,如果记录成功,则偏向锁获取成功,将锁标记位改为 0b01 ,之后如果申请锁的线程ID等于偏向锁绑定的线程ID,就可以直接进入临界区。
锁膨胀机制

目前的JVM对监视器锁采取锁膨胀或锁升级机制,根据实际情况对锁策略进行膨胀升级,其升级方向是:无锁——>偏向锁——>轻量级锁——>重量级锁,锁策略升级之后是无法降级的。具体升级的过程如下:
1.

http://www.xdnf.cn/news/6444.html

相关文章:

  • Redis--基础知识点--26--过期删除策略 与 淘汰策略
  • 聊聊redisson的lockWatchdogTimeout
  • AWS Elastic Beanstalk部署极简Spring工程(EB CLI失败版)
  • 基于OpenCV的人脸微笑检测实现
  • 乘法口诀练习神器
  • 富文本编辑器:链接功能
  • 基于 Python Requests + Pytest + Allure 构建接口自动化测试框架的最优实践
  • 编程日志5.8
  • 【测试】测试分类
  • WebRTC 通话原理:从协商到通信
  • Intellij报错:the file size(3.47M) exceeds configured limit (2.56MB)
  • websocket入门详解
  • 第28周——InceptionV1实现猴痘识别
  • 鸿蒙OSUniApp实现个性化的搜索框与搜索历史记录#三方框架 #Uniapp
  • STM32单片机内存分配详细讲解
  • Android Studio中Gradle 7.0上下项目配置及镜像修改
  • 游戏引擎学习第280天:精简化的流式实体sim
  • 毕设设计 | 管理系统图例
  • ET EntityRef EntityWeakRef 类分析
  • 基于EFISH-SCB-RK3576/SAIL-RK3576的消防机器人控制器技术方案‌
  • VSTO(C#)Excel开发进阶2:操作图片 改变大小 滚动到可视区
  • 产品更新丨谷云科技 iPaaS 集成平台 V7.5 版本发布
  • [特殊字符] 苍穹外卖项目中的 WebSocket 实战:实现来单与催单提醒功能
  • Parsec解决PnP连接失败的问题
  • 星巴克中国要卖在高点
  • sqli-labs靶场第七关——文件导出注入
  • ISP中拖影问题的处理
  • 嵌入式学习笔记DAY21(双向链表、Makefile)
  • C++11(2)
  • MySQL DBA数据运维管理经验分享:新手入门快速提升效率的新工具与技巧