Java程序员必会Synchronized底层原理剖析
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!");
}
}
}
反编译一下,看一下源码实现:

可以看到,底层是通过monitorenter和monitorexit两个关键字实现的加锁与释放锁,执行同步代码之前使用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关键字,还是monitorenter和monitorexit,底层都是通过获取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的基本工作机制:
当多个线程同时访问一段同步代码时,首先会进入 _EntryList 队列中等待。
当某个线程获取到对象的Monitor锁后进入临界区域,并把Monitor中的 _owner 变量设置为当前线程,同时Monitor中的计数器 _count 加1。即获得对象锁。
若持有Monitor的线程调用 wait() 方法,将释放当前持有的Monitor锁,_owner变量恢复为null,_count减1,同时该线程进入 _WaitSet 集合中等待被唤醒。
在_WaitSet 集合中的线程会被再次放到_EntryList 队列中,重新竞争获取锁。
若当前线程执行完毕也将释放Monitor并复位变量的值,以便其他线程进入获取锁。
线程争抢锁的过程要比上面展示得更加复杂。除了_EntryList 这个双向链表用来保存竞争的线程,ObjectMonitor中还有另外一个单向链表 _cxq,由两个队列来共同管理并发的线程。

下篇再讲一下Synchronized锁优化的过程。
我是「一灯架构」,如果本文对你有帮助,欢迎各位小伙伴点赞、评论和关注,感谢各位老铁,我们下期见

Java程序员必会Synchronized底层原理剖析的更多相关文章
- Java程序员必精通之—synchronized
更多Java并发文章:https://www.cnblogs.com/hello-shf/category/1619780.html 一.简介 相信每一个java程序员对synchronized都不会 ...
- Java程序员必了解的JVM原理以及虚拟机的运行过程
JVM概念 虚拟机:指以软件的方式模拟具有完整硬件,VM概念 虚拟机:指以软件的方式模拟具有完整硬件系统功能.运行在一个完全隔离环境中的完整计算机系统 ,是物理机的软件实现.常用的虚拟机有VMWare ...
- Java程序员必学知识点
JVM无论什么级别的Java从业者,JVM都是进阶时必须迈过的坎.不管是工作还是面试中,JVM都是必考题.如果不懂JVM的话,薪酬会非常吃亏(近70%的面试者挂在JVM上了) 详细介绍了JVM有关于线 ...
- 【转】java架构师之路:JAVA程序员必看的15本书的电子版下载地址
作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从.我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想不断提高自己技术水 ...
- Java架构师之路:JAVA程序员必看的15本书
作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从.我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想不断提高自己技术水 ...
- JAVA程序员必看的15本书-JAVA自学书籍推荐
作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从.我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想不断提高自己技术水 ...
- Java程序员必须知道的10个调试技巧
调试可以帮助识别和解决应用程序缺陷,在本文中,将使用大家常用的的开发工具Eclipse来调试Java应用程序. 但这里介绍的调试方法基本都是通用的,也适用于NetBeans IDE,我们会把重点放在运 ...
- Java程序员需要了解的底层知识(一)
硬件基础知识 - Java相关硬件 汇编语言的执行过程(时钟发生器 寄存器 程序计数器) 计算机启动过程 进程线程纤程的基本概念面试高频 - 纤程的实现 内存管理 进程管理与线程管理(进程与线程 ...
- 新一代Java程序员必学的Docker容器化技术基础篇
Docker概述 **本人博客网站 **IT小神 www.itxiaoshen.com Docker文档官网 Docker是一个用于开发.发布和运行应用程序的开放平台.Docker使您能够将应用程序与 ...
随机推荐
- ceph 004 纠删码池 修改参数 cephx认证
复习ceph003 存储池为逻辑概念,存储池可以占用整个集群的所有空间 [root@ceph01 ~]# ceph osd pool create pool1 pool 'pool1' created ...
- 长篇图解java反射机制及其应用场景
一.什么是java反射? 在java的面向对象编程过程中,通常我们需要先知道一个Class类,然后new 类名()方式来获取该类的对象.也就是说我们需要在写代码的时候(编译期或者编译期之前)就知道我们 ...
- “判断性别”Demo需求分析和初步设计(中)
大家好~我开设了"深度学习基础班"的线上课程,带领同学从0开始学习全连接和卷积神经网络,进行数学推导,并且实现可以运行的Demo程序 线上课程资料: 本节课录像回放 加QQ群,获得 ...
- [CF1519C] Berland Regional (数论分块)
题面 有 n 个学生和 n 所大学,每个学生在其中一所大学中学习,且各有一个能力值 s i s_i si . 某次组队打比赛的召集令会给一个数字 k ,表示团队数量.然后每所大学会先把自己的所有学生 ...
- 五 工厂方法模式【Factory Method Pattern】 来自CBF4LIFE 的设计模式
女娲补天的故事大家都听说过吧,今天不说这个,说女娲创造人的故事,可不是"造人"的工作,这个词被现代人滥用了.这个故事是说,女娲在补了天后,下到凡间一看,哇塞,风景太优美了,天空是湛 ...
- 为什么Index Only Scan却还需要访问表
在实际SQL优化工作中,我们经常会发现SQL 执行计划明明是 "Index Only Scan",但执行计划后面却有 "Heap Fetches: x" ,也就 ...
- with function 语法支持
通过with子句,我们可以把很多原本需要存储过程来实现的复杂逻辑用一句SQL来进行表达.KingbaseES 从V008R006C004B0021 版本开始,支持 with function 语法.例 ...
- B树-查找
B树系列文章 1. B树-介绍 2. B树-查找 3. B树-插入 4. B树-删除 查找 假设有一棵3阶B树,如下图所示. 下面说明在该B树中查找52的过程 首先,从根结点出发,根结点有两个键40和 ...
- Java SE 枚举,注解,增强for循环
Java SE 进阶 1.Enum 枚举对象名通常使用全部大写,常量的命名规范 构造器私有化 本类内部创建一组对象 对外暴露对象(通过为对象添加 public final static 修饰符) 可以 ...
- keycloak~资源的远程授权
17.1远程资源授权准备 17.1.1认证和访问流程图 参考:http://www.zyiz.net/tech/detail-141309.html 17.1.2为用户指定角色 可以使用ROLE_US ...