摘要:synchronized锁修饰方法和代码块时底层实现上是一样的,但是在修饰方法时,不需要JVM编译出的字节码完成加锁操作,而synchronized在修饰代码块时,是通过编译出来的字节码生成的monitorenter和monitorexit指令来实现的。

本文分享自华为云社区《Synchronized底层核心原理》,作者: 小威要向诸佬学习呀 。

synchronized锁用于同步实例方法,同步静态方法和同步代码块。自从Java1.6开始,就对synchronized锁进行了很多方面的优化。对其引入了偏向锁,轻量级锁,适应性自旋锁,锁粗化,锁消除等各种技术方面的优化。

synchronized锁是基于monitor锁实现的,因此在讲解synchronized锁之前,有必要了解一下monitor锁。

monitor锁的原理

monitor,在中文中有监视器的意思,当创建对象时,每一个创建出来的对象都会关联一个monitor对象,对于一个java对象,当拿到这个monitor对象时,这个monitor对象就会处于锁定的状态,其他对象不会再获取,synchronized锁的本质就是基于进入和退出monitor对象实现的同步方法和同步代码块。

这里首先解释一下wait,notify,notifyAll等方法的各个作用:

wait方法会让进入object监视器的线程进入到WaitSet集合中等待;
notify方法会使在object上正在WaitSet集合上等待的线程中挑一个唤醒线程;
notifyAll方法会让正在WaitSet集合中等待的线程全部唤醒。

而对于monitor,它是基于ObjectMonitor实现的,ObjectMonitor的主要数据结构包括:

owner:owner原本的值为null,它用来指向获取到ObjectMonitor对象的线程。当一个线程获取到ObjectMonitor对象时,这个ObjectMonitor对象就会存储在当前对象的对象头中的Mark
Word中。

WaitSet,这个是ObjectMonitor中的一个集合,同时WaitSet与wait()方法有关。当Owner线程发现条件不满足时,会调用wait方法,使线程进入WaitSet集合中变为WAITING状态。

EntryList,也是ObjectMonitor中的一个集合,同时EntryList与notify(),notifyAll()方法有关。WAITING状态下的线程会在Owner线程调用notify()或notifyAll()等方法时唤醒,但是唤醒之后并不代表着线程会立即拿到锁资源,而是需要进入EntryList集合中进行竞争。

模拟多线程情况下,同时访问一个被synchronized锁修饰方法时,在JVM底层中的流程如下·:

  1. 线程进入EntryList集合时,如果某个线程获取到monitor对象时,这个线程会进入owner中,同时会把monitor对象中的owner变量复制为当前的线程(拿到monitor对象的这个),并且会把monitor对象中的count变量值+1。
  2. 如果线程调用wait方法,当前的线程就会释放拿到的monitor对象,并且会把monitor对象中的owner变量值设为null,并且count的值-1。最后,当前线程会进入到WaitSet集合中等待,等候再次被唤醒。
  3. 如果是获得monitor对象的线程执行任务完成后,也会进行上面的一系列操作,但不会到WaitSet集合中等待了,因为任务已经执行完了。

synchronized修饰方法

前面说到synchronized锁是基于monitor锁实现的。当synchronized锁修饰方法时,被此锁修饰的方法会比普通方法的常量池中多一个ACC_SYNCHRONIZED标识符。当线程调用了被synchronized锁修饰的方法时,会检查方法中是否设置了此标识符。

如果设置了ACC_SYNCHRONIZED标识符,那么当前的线程会首先获取monitor锁对象,然后执行同步代码中的方法,完成后会释放monitor对象。当然,在多线程情况下,只有一个线程能够获取此monitor对象,并且在该线程释放monitor对象之前,其他线程无法获取此monitor对象。因此在同一时刻,只能有一个线程拿到相同对象的synchronized锁资源。

而当synchronized锁修饰代码块时,与synchronized修饰方法略有不同,接下来详细讲解synchronized修饰代码块的情况。

synchronized修饰代码块

当synchronized锁修饰代码块时,synchronized关键字会被编译成monitorenter和monitorexit两条指令,其中,monitorenter会放在代码块的前面,而monitorexit会放在代码块的后面。

对于monitorenter指令:

每个对象都拥有一个monitor,当monitor被占用时,就会处于锁定状态,线程执行monitorenter指令时会获取monitor的所有权。

当monitor计数为0时,说明该monitor还未被锁定,此时线程会进入monitor并将monitor的计数器设为1,并且该线程就是monitor的所有者。

如果此线程已经获取到了monitor锁,再重新进入monitor锁的话,那么会将计时器count的值加1。

如果有线程已经占用了monitor锁,此时有其他的线程来获取锁,那么此线程将进入阻塞状态,待monitor的计时器count变为0,这个线程才会获取到monitor锁。

对于monitorexit指令:

首先,只有拿到了monitor锁对象的线程才会执行monitorexit指令。

其次就是,在执行monitorexit指令时,计时器count的值会减1,当count的值减到0时,当前的线程才会退出monitor,此时的线程不再是monitor的所有者,当然执行后,其他线程可以获取当前monitor锁的所有权。

通过对简单代码进行反编译来举例:

public class SynchronizedTest {
public void synchronize(){
synchronized (this){
System.out.println("hello world");
}
}
}

执行 javap -c SynchronizedTest.class指令得到以下字节码:

public class Synchronized.SynchronizedTest {
public Synchronized.SynchronizedTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void synchronize();
Code:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String hello world
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_1
13: monitorexit
14: goto 22
17: astore_2
18: aload_1
19: monitorexit
20: aload_2
21: athrow
22: return
Exception table:
from to target type
4 14 17 any
17 20 17 any
}

由上述编码可以看出,在synchronized修饰的代码块中,存在有monitorenter指令和monitorexit指令。

synchronized锁总结

因此,由以上可以得出,synchronized锁修饰方法和代码块时底层实现上是一样的,但是在修饰方法时,不需要JVM编译出的字节码完成加锁操作,而synchronized在修饰代码块时,是通过编译出来的字节码生成的monitorenter和monitorexit指令来实现的。

点击关注,第一时间了解华为云新鲜技术~

想会用synchronized锁,先掌握底层核心原理的更多相关文章

  1. synchronized底层实现原理&CAS操作&偏向锁、轻量级锁,重量级锁、自旋锁、自适应自旋锁、锁消除、锁粗化

    进入时:monitorenter 每个对象有一个监视器锁(monitor).当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:1 ...

  2. 【从刷面试题到构建知识体系】Java底层-synchronized锁-1

    在技术论坛中,经常看到一种言论:面试造火箭,干活拧螺丝.我们平时写的大部分代码的确是CRDU,再提一个层次,也无非就是揉进去复杂一些的业务逻辑,把一堆的CRDU组合起来. 那么问题来了:我们提倡的研究 ...

  3. 【从刷面试题到构建知识体系】Java底层-synchronized锁-2偏向锁篇

    上一篇通过构建金字塔结构,来从不同的角度,由浅入深的对synchronized关键字做了介绍, 快速跳转:https://www.cnblogs.com/xyang/p/11631866.html 本 ...

  4. synchronized底层实现原理及锁优化

    一.概述 1.synchronized作用 原子性:synchronized保证语句块内操作是原子的 可见性:synchronized保证可见性(通过"在执行unlock之前,必须先把此变量 ...

  5. 记一次synchronized锁字符串引发的坑兼再谈Java字符串

    问题描述 业务有一个需求,我把问题描述一下: 通过代理IP访问国外某网站N,每个IP对应一个固定的网站N的COOKIE,COOKIE有失效时间.并发下,取IP是有一定策略的,取到IP之后拿IP对应的C ...

  6. 记一次 synchronized 锁字符串引发的坑兼再谈 Java 字符串

    业务有一个需求,我把问题描述一下: 通过代理IP访问国外某网站N,每个IP对应一个固定的网站N的COOKIE,COOKIE有失效时间. 并发下,取IP是有一定策略的,取到IP之后拿IP对应的COOKI ...

  7. Synchronized锁性能优化偏向锁轻量级锁升级 多线程中篇(五)

    不止一次的提到过,synchronized是Java内置的机制,是JVM层面的,而Lock则是接口,是JDK层面的 尽管最初synchronized的性能效率比较差,但是随着版本的升级,synchro ...

  8. 015-线程同步-synchronized几种加锁方式、Java对象头和Monitor、Mutex Lock、JDK1.6对synchronized锁的优化实现

    一.synchronized概述基本使用 为确保共享变量不会出现并发问题,通常会对修改共享变量的代码块用synchronized加锁,确保同一时刻只有一个线程在修改共享变量,从而避免并发问题. syn ...

  9. Synchronized锁升级原理与过程深入剖析

    Synchronized锁升级原理与过程深入剖析 前言 在上篇文章深入学习Synchronized各种使用方法当中我们仔细介绍了在各种情况下该如何使用synchronized关键字.因为在我们写的程序 ...

  10. 【多线程与高并发】- synchronized锁的认知

    synchronized锁的认知 生命不息,写作不止 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目录 s ...

随机推荐

  1. iframe用的场景

    iframe可用在以下几个场景中: 1:典型系统结构,左侧是功能树,右侧就是一些常见的table或者表单之类的.为了每一个功能,单独分离出来,采用iframe. 2:ajax上传文件. 3:加载别的网 ...

  2. GameFramework摘录 - 2.访问器模式(Version、Logger)

    访问器模式 Version.Logger等基础模块,功能相对固定但拥有几套不同的行为(如开发版本和正式版本不同),采用访问器模式,便于调整功能或复用 public static class Versi ...

  3. JAVA类的加载(2) ——按需加载(延迟加载)

    1.例1: 1 /* 2 按需加载:当你不去实例化Cat时,Cat相关类都不会被加载,即按需加载(需要时加载) 3 1.先加载父类 4 2.初始化类 5 3.类只加载一次(暂且这么认为)--缓存 6 ...

  4. 如何解决Asp.Net Core 3.1上传文件出现跨域

    这个问题挺奇怪的,明明就是文件过大的问题,却出现了跨域的错误,搞不懂,有了解的大佬请指教. 但问题还是解决了,其实就是Nginx默认上传大小限制为1M,如果超出了,则出现跨域的错误. 一.自定义Ngi ...

  5. CSS 浮动和清除浮动方法总结

    作者:WangMin 格言:努力做好自己喜欢的每一件事 什么是浮动 float? 标准流:盒子会各占整行位置.子盒子若是标准流,父盒子虽然没有高度,但是会撑开父盒子高度. 浮动:盒子浮了起来,不会占据 ...

  6. 提高cin cout的速度

     ios::sync_with_stdio(false) :用来关闭iostream与stdio的同步,从而提高 cin cout 的效率,但是就不能再用 scanf printf 了,因为不关闭之前 ...

  7. offline RL | TD3+BC:在最大化 Q advantage 时添加 BC loss 的极简算法

    题目:A Minimalist Approach to Offline Reinforcement Learning ,NeurIPS 2021,8 7 7 5. pdf 版本:https://arx ...

  8. 电子元器件工厂的金蝶 ERP 与赛意 WMS 系统数据集成平台进行对接

    项目背景 国内某晶振集成电路研发单位,涵盖从产品开发.设计.生产.销售.服务等各个环节.需要全面建成以ERP.WMS.BOM.PLM.DMS.SRM.OA 为核心的企业信息系统,支持研发.生产.营销. ...

  9. 黑客玩具入门——5、继续Metasploit

    1.利用FTP漏洞并植入后门 实验靶机:Metasploitable2. 实践: 使用nmap扫描目标靶机 nmap -sV xxx.xxx.xxx.xxx(目标ip) 生成linux系统后门 msf ...

  10. [CF1601C] Optimal Insertion

    Optimal Insertion 题面翻译 题目大意 给定两个序列 \(a,b\),长度分别为 \(n,m(1\leq n,m\leq 10^6)\).接下来将 \(b\) 中的所有元素以任意方式插 ...