synchronized作为Java程序员最常用同步工具,很多人却对它的用法和实现原理一知半解,以至于还有不少人认为synchronized是重量级锁,性能较差,尽量少用。

但不可否认的是synchronized依然是并发首选工具,连volatile、CAS、ReentrantLock都无法动摇synchronized的地位。synchronized是工作面试中的必备技能,今天就跟着一灯一块深入剖析synchronized的底层原理。

1. synchronized作用

synchronized是Java提供一种隐式锁,无需开发者手动加锁释放锁。保证多线程并发情况下数据的安全性,实现了同一个时刻只有一个线程能访问资源,其他线程只能阻塞等待,简单说就是互斥同步。

2. synchronized用法

先看一下synchronized有哪几种用法?

使用位置 被锁对象 示例代码
实例方法 实例对象 public synchronized void method() {
……
}
静态方法 class类 public static synchronized void method() {
……
}
实例对象 实例对象 public void method() {
Object obj = new Object();
synchronized (obj) {
……
}
}
类对象 class类 public void method() {
synchronized (Demo.class) {
……
}
}
this关键字 实例对象 public void method() {
synchronized (this) {
……
}
}

可以看到被锁对象只要有两种,实例对象和class类。

  • 由于静态方法可以通过类名直接访问,所以它跟直接加锁在class类上是一样的。

  • 当在实例方法、实例对象、this关键字上面加锁的时候,锁定范围都是当前实例对象。

  • 实例对象上面的锁和class类上面的锁,两者不互斥。

3. synchronized加锁原理

当我们使用synchronized在方法和对象上加锁的时候,Java底层到底怎么实现加锁的?

当在类对象上加锁的时候,也就是在class类加锁,代码如下:

/**
* @author 一灯架构
* @apiNote Synchronized示例
**/
public class SynchronizedDemo { public void method() {
synchronized (SynchronizedDemo.class) {
System.out.println("Hello world!");
}
} }

反编译一下,看一下源码实现:

可以看到,底层是通过monitorentermonitorexit两个关键字实现的加锁与释放锁,执行同步代码之前使用monitorenter加锁,执行完同步代码使用monitorexit释放锁,抛出异常的时候也是用monitorexit释放锁。

写成伪代码,类似下面这样:

/**
* @author 一灯架构
* @apiNote Synchronized示例
**/
public class SynchronizedDemo { public void method() {
try {
monitorenter 加锁;
System.out.println("Hello world!");
monitorexit 释放锁;
} catch (Exception e) {
monitorexit 释放锁;
}
} }

当在实例方法上加锁,底层是怎么实现的呢?代码如下:

/**
* @author 一灯架构
* @apiNote Synchronized示例
**/
public class SynchronizedDemo { public static synchronized void method() {
System.out.println("Hello world!");
} }

再反编译看一下底层实现:

这次只使用了一个ACC_SYNCHRONIZED关键字,实现了隐式的加锁与释放锁。其实无论是ACC_SYNCHRONIZED关键字,还是monitorentermonitorexit,底层都是通过获取monitor锁来实现的加锁与释放锁。

monitor锁又是通过ObjectMonitor来实现的,虚拟机中ObjectMonitor数据结构如下(C++实现的):

ObjectMonitor() {
_header = NULL;
_count = 0; // WaitSet 和 EntryList 的节点数之和
_waiters = 0,
_recursions = 0; // 重入次数
_object = NULL;
_owner = NULL; // 持有锁的线程
_WaitSet = NULL; // 处于wait状态的线程,会被加入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ; // 多个线程争抢锁,会先存入这个单向链表
FreeNext = NULL ;
_EntryList = NULL ; // 处于等待锁block状态的线程,会被加入到该列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}

图上展示了ObjectMonitor的基本工作机制:

  1. 当多个线程同时访问一段同步代码时,首先会进入 _EntryList 队列中等待。

  2. 当某个线程获取到对象的Monitor锁后进入临界区域,并把Monitor中的 _owner 变量设置为当前线程,同时Monitor中的计数器 _count 加1。即获得对象锁。

  3. 若持有Monitor的线程调用 wait() 方法,将释放当前持有的Monitor锁,_owner变量恢复为null,_count减1,同时该线程进入 _WaitSet 集合中等待被唤醒。

  4. 在_WaitSet 集合中的线程会被再次放到_EntryList 队列中,重新竞争获取锁。

  5. 若当前线程执行完毕也将释放Monitor并复位变量的值,以便其他线程进入获取锁。

线程争抢锁的过程要比上面展示得更加复杂。除了_EntryList 这个双向链表用来保存竞争的线程,ObjectMonitor中还有另外一个单向链表 _cxq,由两个队列来共同管理并发的线程。

下篇再讲一下Synchronized锁优化的过程。

我是「一灯架构」,如果本文对你有帮助,欢迎各位小伙伴点赞、评论和关注,感谢各位老铁,我们下期见

Java程序员必会Synchronized底层原理剖析的更多相关文章

  1. Java程序员必精通之—synchronized

    更多Java并发文章:https://www.cnblogs.com/hello-shf/category/1619780.html 一.简介 相信每一个java程序员对synchronized都不会 ...

  2. Java程序员必了解的JVM原理以及虚拟机的运行过程

    JVM概念 虚拟机:指以软件的方式模拟具有完整硬件,VM概念 虚拟机:指以软件的方式模拟具有完整硬件系统功能.运行在一个完全隔离环境中的完整计算机系统 ,是物理机的软件实现.常用的虚拟机有VMWare ...

  3. Java程序员必学知识点

    JVM无论什么级别的Java从业者,JVM都是进阶时必须迈过的坎.不管是工作还是面试中,JVM都是必考题.如果不懂JVM的话,薪酬会非常吃亏(近70%的面试者挂在JVM上了) 详细介绍了JVM有关于线 ...

  4. 【转】java架构师之路:JAVA程序员必看的15本书的电子版下载地址

    作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从.我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想不断提高自己技术水 ...

  5. Java架构师之路:JAVA程序员必看的15本书

    作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从.我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想不断提高自己技术水 ...

  6. JAVA程序员必看的15本书-JAVA自学书籍推荐

    作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从.我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想不断提高自己技术水 ...

  7. Java程序员必须知道的10个调试技巧

    调试可以帮助识别和解决应用程序缺陷,在本文中,将使用大家常用的的开发工具Eclipse来调试Java应用程序. 但这里介绍的调试方法基本都是通用的,也适用于NetBeans IDE,我们会把重点放在运 ...

  8. Java程序员需要了解的底层知识(一)

    硬件基础知识 - Java相关硬件 汇编语言的执行过程(时钟发生器  寄存器  程序计数器) 计算机启动过程 进程线程纤程的基本概念面试高频 -  纤程的实现 内存管理 进程管理与线程管理(进程与线程 ...

  9. 新一代Java程序员必学的Docker容器化技术基础篇

    Docker概述 **本人博客网站 **IT小神 www.itxiaoshen.com Docker文档官网 Docker是一个用于开发.发布和运行应用程序的开放平台.Docker使您能够将应用程序与 ...

随机推荐

  1. Excel 查找函数(一):LOOKUP

    序号 员工姓名 部门 职务 1 苏霞 法务部 法律顾问 2 包志林 财务部 财务总监 3 林娥云 安监部 部长 4 石少卿 质检部 质检员 5 于炳福 生产部 生产部 6 蒋琼志 仓储部 保管员 7 ...

  2. docker启动失败问题

    内核3.10,systemctl start docker 被阻塞,没有返回,查看状态为启动中. 某兄弟机器安装docker之后,发现systemctl start docker的时候阻塞,由于排查走 ...

  3. 数据库简介与MySQL简介

    MySQL简介 数据存取演变史 起源······文本文件 在最开始使用计算机都没有相应的规范我们的数据一般都是自己起一个名字然后就根据这个路径存储数据并且存储数据的格式也都五花八门就产生了很多奇奇怪怪 ...

  4. 使用 Mypy 检查 30 万行 Python 代码,总结出 3 大痛点与 6 个技巧!

    作者:Charlie Marsh 译者:豌豆花下猫@Python猫 英文:Using Mypy in production at Spring (https://notes.crmarsh.com/u ...

  5. 三分钟,带你了解PLM

    PLM应用于单一地点或者多个地点的企业内部.以及在产品研发领域具有协作关系的企业之间的.支持产品全生命周期的信息的创建.管理.分发和应用的综合性的应用解决方案,能够集成与产品相关的流程.应用系统和信息 ...

  6. WSUS连接错误需要重置服务器

    在WSUS完成部署后,总是遇到控制台错误,提示需要重置服务器节点.https://www.cnblogs.com/qishine/p/12727982.html错误:连接错误尝试连接WSUS服务器时出 ...

  7. Openstack neutron:云数据中心底层网络架构

    目录 - 目录 - 云数据中心流量类型 - NSX整体网络结构 - 管理网络(API网络) - 租户网络 - 外联网络 - 存储网络 - openstack整体网络结构 - 管理网络:(上图中蓝线) ...

  8. G&GH05 删除文件和.gitignore

    注意事项与声明 平台: Windows 10 作者: JamesNULLiu 邮箱: jamesnulliu@outlook.com 博客: https://www.cnblogs.com/james ...

  9. Python数据科学手册-机器学习: 决策树与随机森林

    无参数 算法 随机森林 随机森林是一种集成方法,集成多个比较简单的评估器形成累计效果. 导入标准程序库 随机森林的诱因: 决策树 随机森林是建立在决策树 基础上 的集成学习器 建一颗决策树 二叉决策树 ...

  10. 基于 OpenTelemetry 的链路追踪

    链路追踪的前世今生 分布式跟踪(也称为分布式请求跟踪)是一种用于分析和监控应用程序的方法,尤其是使用微服务架构构建的应用程序.分布式跟踪有助于精确定位故障发生的位置以及导致性能差的原因. 起源 链路追 ...