synchronized凭什么锁得住?
相关链接:
我们知道synchronized是重量级锁,我们知道synchronized锁住的是一个对象上的Monitor对象,我们也知道synchronized用于同步代码块时会执行monitorenter和monitorexit等。
上面几个问题仅仅是校招级。
那么synchronized为什么“重”呢?Monitor对象从何而来呢?synchronized用于实例方法或者静态方法又是怎么锁住的呢?
在《synchronized锁住的是谁?》中我们明确了,synchronized锁住的对象,本文讲述synchronized凭什么锁得住。
首先我们需要知道的是在Hotspot虚拟机实现中,对象实例在堆内存中结构分为3个部分:对象头、实例数据、对其填充字节。在Java中万物皆为对象。就算一个Java类被编译称为class二进制文件在被加载到内存时,它仍然会在堆内存中创建一个Class对象。这也就解释了,为什么synchronized能对类加锁(因为每个类在堆内存中有一个Class对象,对于类synchronized锁的实际上是Class对象,下文会继续解释)。
在解释了Java中对象实例在Hotspot中的内存结构(对象头、实例数据、对其填充字节)后,synchronized锁住的Monitor对象就存在于对象头之中。对象头又分为:Mark Word、指向类的指针、数组长度(数组对象)。
对象头在Hotspot虚拟机实现中,分为32位和64位的实现,实际上Hotspot源代码实现中的注释已经解释得非常清楚了(openjdk/hotspot/share/oops/markOop.hpp),对象头的Mark Word位格式在32位机器中是32位长,在64位机器中是64位长(采用 big endian ,低地址存放最高有效字节,即低位在左,高位再右)。
|
32bit位虚拟机Mark Word |
|||||
|
锁状态 |
25bit |
4bit |
1bit |
2bit |
|
|
23bit |
2bit |
是否是偏向锁 |
锁标志位 |
||
|
无锁状态 |
对象的hashcode |
分代年龄 |
0 |
01 |
|
|
偏向锁 |
线程ID |
偏向时间戳 |
分代年龄 |
1 |
01 |
|
轻量级锁 |
指向栈中锁记录的指针 |
00 |
|||
|
重量级锁 |
指向重量级锁(Monitor)的指针 |
10 |
|||
|
GC标记 |
空 |
11 |
|||
和synchronized相关的就是Java在Hotspot虚拟机实现中对象头中的Mark Word。
在以前(JDK5之前),synchronized被称为重量级锁是无可厚非的,但在JDK6后,JVM对其进行了一系列优化,尽量使得synchronized不再那么重。之所以synchronized重,是因为它涉及到了操作系统用户态与核心态的转换,下文再详细解释。这里我们从最轻的偏向锁->轻量级锁->重量级锁的过程,注意他们只能升级加锁的强度,不能降级。
偏向锁
上面提到了JDK6过后优化了synchronized的加锁过程,尽量使得synchronized不再那么重。偏向锁即是如此。
JVM的研究者表明,大多数情况下锁的竞争不是那么激励,在不那么激励的时候如果通过获取Monitor来进行同步访问,会造成线程在操作系统用户态和核心态的转换,这会使得系统性能下降。偏向锁表示,当只有一个线程进入同步方法或同步代码块时,并不会直接获取Monitor锁,而是先判断对象头中Mark Word部分的锁标志位是否处于“01”,如果处于“01”,此时再判断线程ID是否是本线程ID,如果是则直接进入方法进行后续操作;如果不是,此时则通过CAS(无锁机制竞争)如果竞争成功,此时将线程ID设置为本线程ID,如果竞争失败,说明造成了有了较为强烈的锁竞争,偏向锁已不能满足,此时偏向锁晋级为轻量级锁。
轻量级锁
当锁发生竞争时,持有偏向锁的线程会撤销偏向锁,转而晋级为轻量级锁(状态)。轻量级锁的核心是,不让未获取锁的线程进入阻塞状态,因为这会使得线程由用户态转为核心态,这会造成很大的性能损失,而是采用“死循环”的方式不断的获取锁,这种采用“死循环”获取的锁的方式称为——锁自旋。它不会让线程陷入阻塞,但同时仅适用于持有锁时间较短的场景。那么轻量级锁升级为重量级锁的条件就是,自旋等待的时间过长,并且又有了新的线程来竞争。
重量级锁
这种锁,就是地地道道原原本本synchronized的本意了。线程会去抢夺对象上的一个互斥量(这个互斥量就是Monitor),每个对象都会有,就算是类也有一个Monitor互斥量(因为类在堆内存中有一个Class对象)。当一个线程获取到对象的Monitor锁时,其余线程会被阻塞挂起,并且由用户态转为核心态。
上文提到在锁的竞争状态晋级为重量级锁时,Java对象头中的Mark Word前30位存储的是Monitor对象的指针。Monitor对象定义在openjdk/hotspot/share/runtime/objectMonitor.hpp中,在ObjectMonitor中定义了:计数器、持有Monitor的线程、处于wait状态的线程、处于阻塞状态的线程等等。
synchronized无论是普通实例还是同步代码块,它所获取的锁是对象实例中的Monitor锁,而对象的Monitor又是存在于Java对象头的Mark Work之中,所以可以这么说,synchronized获取的锁在Java对象头中。对于普通实例或者静态方法,JVM并没有显示的指令进入临界区,而是在方法上标识了“ACC_SYNCHRONIZED”,标识是synchronized同步方法,方法内部都是临界区。而对于同步代码块,则在synchronized代码块开始执行了monitorenter,结束或者抛出异常时执行了monitorexit指令。
synchronized凭借的就是Monitor锁住的对象,Monitor又是借助于操作系统的mutex lock,之所以它重是因为它被挂起后线程会由用户态转换为内核态,这个转换会带来性能损耗。JDK6开始对其进行了优化,提出了偏向锁和轻量级锁,针对锁竞争较为激烈的场景不会直接去获取Monitor对象,减少性能损耗。因此在现如今的synchronized实现中,它的性能劣势也已不再那么明显。
这是一个能给程序员加buff的公众号 (CoderBuff)

synchronized凭什么锁得住?的更多相关文章
- synchronized 作为悲观锁,锁住了什么?
继续来认识 synchronized,上篇文章加不加 synchronized 有什么区别?我们了解了 synchronized 是在多线程并发竞争同一资源的时候使用,这一篇我们来了解,synchro ...
- (转)Synchronized(对象锁)和Static Synchronized(类锁)的区别
场景:面试的时候经常用得到! 1 综述 Synchronized和Static Synchronized区别 一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全 ...
- 精通java并发-synchronized关键字和锁
目前CSDN,博客园,简书同步发表中,更多精彩欢迎访问我的gitee pages synchronized关键字和锁 示例代码 public class MyThreadTest2 { public ...
- synchronized是对象锁还是全局锁
昆昆欧粑粑 2019-02-20 15:09:59 1148 收藏 1分类专栏: java学习 文章标签: synchronized 全局锁 对象锁 同步版权都可以锁!synchronized(thi ...
- synchronized内置锁
synchronized内置锁,如果发生阻塞,无法被中断,除非关闭jvm.因此不能从死锁中恢复.
- Java 线程锁机制 -Synchronized Lock 互斥锁 读写锁
(1)synchronized 是互斥锁: (2)ReentrantLock 顾名思义 :可重入锁 (3)ReadWriteLock :读写锁 读写锁特点: a)多个读者可以同时进行读b)写者必须互斥 ...
- Java多线程简析——Synchronized(同步锁)、Lock以及线程池
Java多线程 Java中,可运行的程序都是有一个或多个进程组成.进程则是由多个线程组成的.最简单的一个进程,会包括mian线程以及GC线程. 线程的状态 线程状态由以下一张网上图片来说明: 在图中, ...
- Java多线程系列 基础篇06 synchronized(同步锁)
转载 http://www.cnblogs.com/paddix/ 作者:liuxiaopeng http://www.infoq.com/cn/articles/java-se-16-synchro ...
- synchronized:内部锁
synchronized:内部锁 起源: 并行程序开发涉及多线程.多任务间的协作和数据共享 一).内部锁:synchronized 1).定义在方法上 public synchronized void ...
随机推荐
- cesium 实现 3d-tiles 平移旋转贴地(附源码下载)
前言 cesium 官网的api文档介绍地址cesium官网api,里面详细的介绍 cesium 各个类的介绍,还有就是在线例子:cesium 官网在线例子,这个也是学习 cesium 的好素材. 内 ...
- 马拉车manacher
目的:线性查找一个串的最长回文子串 时间复杂度:O(n) len[i]表示以i为中心的回文串的半径,mx即为当前计算回文串最右边字符的最大值,p是中心点mid,mx-i和2*p-1关于p对称 http ...
- HDU5002 tree
You are given a tree with N nodes which are numbered by integers 1..N. Each node is associated with ...
- python sympy evalf()函数
SymPy是一个符号计算的Python库.它的目标是成为一个全功能的计算机代数系统,同时保持代码简 洁.易于理解和扩展.它完全由Python写成,不依赖于外部库.SymPy支持符号计算.高精度计算.模 ...
- 使用Spring Boot和AspectJ实现方法跟踪基础结构
了解如何使用Spring Boot和AspectJ实现方法跟踪基础结构!最近在优锐课学习收获颇多,记录下来大家一起进步! 在我们的应用程序中,获取方法的堆栈跟踪信息可能会节省很多时间.具有输入输出参数 ...
- wxxcx_learn异常处理
属于基础框架,分级别 捕获异常,处理异常(记录日志,修复异常,返回客户端错误),抛出异常 全局异常处理(AOP)对错误同一格式化 try{ $banner = BannerModel::getBann ...
- 深入探索Java设计模式(三)之装饰器模式
装饰器模式使你可以在运行时使用类似于对象组成的技术来装饰类.这在我们希望实例化具有新职责的对象而无需对基础类进行任何代码更改的情况下尤其有用.本文是在学习完优锐课JAVA架构VIP课程—[框架源码专题 ...
- Linux搭建NFS服务
一.NFS服务简单介绍 NFS(Network File System)即网络文件系统,是FreeBSD支持的文件系统中的一种,它允许网络中的计算机之间通过TCP/IP网络共享资源.在NFS的应用中, ...
- 可编程逻辑(FPGA)与硬核处理器(HPS)之间互联的结构
本周我想进一步探究可编程逻辑(FPGA)与硬核处理器(HPS)之间互联的结构.我发现了三种主要方式,它们是如何映射并处理通信的,哪些组件需要管控时序并且有访问权限. AXI Bridge 为了能够实现 ...
- 牛客练习赛34 little w and Segment Coverage (差分区间)
链接:https://ac.nowcoder.com/acm/contest/297/C来源:牛客网 题目描述 小w有m条线段,编号为1到m. 用这些线段覆盖数轴上的n个点,编号为1到n. 第i条线段 ...