JUC编程之——synchronized的底层实现与分析

news2024/11/27 5:36:12

1 synchronized关键字

synchronized 是 Java 中的关键字,是一种同步锁(也是一种悲观锁)。它修饰的对象有以下几种:

  • 作用于实例方法,当前实例加锁,进入同步代码前要获得当前实例的锁——对象锁
  • 作用于代码块,对括号里配置的对象加锁——对象锁
  • 作用于静态方法,当前类加锁,进去同步代码前要获得当前类对象的锁——类锁

注:synchronized不能修饰变量;

2 javap指令

javap是jdk自带的反解析工具。 它的作用就是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。

javap -c
  • -c代表对代码进行反汇编;
  • javap -v ***.class文件反编译
  • -v -verbose 输出附加信息,如行号、本地变量表、反汇编等更加详细的信息;

3 从字节码角度分析synchronized实现

3.1 同步代码块

代码示例

public class LockSyncTest {

    // 一个实例对象
    Object object = new Object();

    public void m1(){
        // 同步代码块
        synchronized (object) {
            System.out.println("这里是同步代码块");
        }
    }

    public static void main(String[] args) {

    }
}

反编译

javap -c LockSyncTest.class

生成内容

Compiled from "LockSyncTest.java"
public class LockSyncTest {
  java.lang.Object object;

  public LockSyncTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: new           #2                  // class java/lang/Object
       8: dup
       9: invokespecial #1                  // Method java/lang/Object."<init>":()V
      12: putfield      #3                  // Field object:Ljava/lang/Object;
      15: return

  public void m1();
    Code:
       0: aload_0
       1: getfield      #3                  // Field object:Ljava/lang/Object;
       4: dup
       5: astore_1
       // 获得监视器并进入
       6: monitorenter
       7: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;        
      // here
      10: ldc           #5                  // String 这里是同步代码块
      12: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      15: aload_1
      // 锁退出
      16: monitorexit
      17: goto          25
      20: astore_2
      21: aload_1
      // 底层做了异常处理 如果程序出现异常 底层也要保证释放锁
      22: monitorexit
      23: aload_2
      24: athrow
      25: return
    Exception table:
       from    to  target type
           7    17    20   any
          20    23    20   any

  public static void main(java.lang.String[]);
    Code:
       0: return
}

两个重要指令

  • monitorenter,获得锁;
  • monitorexit,释放锁(两个,一个底层实现发生异常时也要释放锁);

一定是一个enter两个exit么?
——不一定,如果自己在同步代码块throw异常,只有一个enter一个exit;

3.2 普通同步方法

代码示例

public class LockSyncTest {


    // 普通同步方法
    public synchronized void m2(){
        System.out.println("普通同步方法");
    }

    public static void main(String[] args) {

    }
}


反编译

javap -v LockSyncTest.class

生成内容

调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程会将先持有monitor然后再执行方法,最后再方法完成时(无论是正常完成还是非正常完成)时释放monitor;

Classfile out/production/ch16/LockSyncTest.class
  Last modified 2023-4-11; size 608 bytes
  MD5 checksum ff2328e7a59f847a473b7ed814cc108b
  Compiled from "LockSyncTest.java"
public class LockSyncTest
  SourceFile: "LockSyncTest.java"
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#21         //  java/lang/Object."<init>":()V
   #2 = Fieldref           #22.#23        //  java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #24            //  普通同步方法
   #4 = Methodref          #25.#26        //  java/io/PrintStream.println:(Ljava/lang/String;)V        
   #5 = Class              #27            //  LockSyncTest
   #6 = Class              #28            //  java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               LLockSyncTest;
  #14 = Utf8               m2
  #15 = Utf8               main
  #16 = Utf8               ([Ljava/lang/String;)V
  #17 = Utf8               args
  #18 = Utf8               [Ljava/lang/String;
  #19 = Utf8               SourceFile
  #20 = Utf8               LockSyncTest.java
  #21 = NameAndType        #7:#8          //  "<init>":()V
  #22 = Class              #29            //  java/lang/System
  #23 = NameAndType        #30:#31        //  out:Ljava/io/PrintStream;
  #24 = Utf8               普通同步方法
  #25 = Class              #32            //  java/io/PrintStream
  #26 = NameAndType        #33:#34        //  println:(Ljava/lang/String;)V
  #27 = Utf8               LockSyncTest
  #28 = Utf8               java/lang/Object
  #29 = Utf8               java/lang/System
  #30 = Utf8               out
  #31 = Utf8               Ljava/io/PrintStream;
  #32 = Utf8               java/io/PrintStream
  #33 = Utf8               println
  #34 = Utf8               (Ljava/lang/String;)V
{
  public LockSyncTest();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       5     0  this   LLockSyncTest;

  public synchronized void m2();
    // ACC_SYNCHRONIZED标识
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;      
         3: ldc           #3                  // String 普通同步方法
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 6: 0
        line 7: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       9     0  this   LLockSyncTest;

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       1     0  args   [Ljava/lang/String;
}

3.3 静态同步方法

代码示例

public class LockSyncTest {


    // 静态同步方法
    public static synchronized void m3(){
        System.out.println("静态同步方法");
    }

    public static void main(String[] args) {

    }
}


反编译

javap -v LockSyncTest.class

生成内容

ACC_STATIC, ACC_SYNCHRONIZED标识符,区分方法是否为静态同步方法;

Classfile out/production/ch16/LockSyncTest.class
  Last modified 2023-4-11; size 590 bytes
  MD5 checksum e551241d2333dd7b754d24e72063d547
  Compiled from "LockSyncTest.java"
public class LockSyncTest
  SourceFile: "LockSyncTest.java"
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#21         //  java/lang/Object."<init>":()V
   #2 = Fieldref           #22.#23        //  java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #24            //  静态同步方法
   #4 = Methodref          #25.#26        //  java/io/PrintStream.println:(Ljava/lang/String;)V        
   #5 = Class              #27            //  LockSyncTest
   #6 = Class              #28            //  java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               LLockSyncTest;
  #14 = Utf8               m3
  #15 = Utf8               main
  #16 = Utf8               ([Ljava/lang/String;)V
  #17 = Utf8               args
  #18 = Utf8               [Ljava/lang/String;
  #19 = Utf8               SourceFile
  #20 = Utf8               LockSyncTest.java
  #21 = NameAndType        #7:#8          //  "<init>":()V
  #22 = Class              #29            //  java/lang/System
  #23 = NameAndType        #30:#31        //  out:Ljava/io/PrintStream;
  #24 = Utf8               静态同步方法
  #25 = Class              #32            //  java/io/PrintStream
  #26 = NameAndType        #33:#34        //  println:(Ljava/lang/String;)V
  #27 = Utf8               LockSyncTest
  #28 = Utf8               java/lang/Object
  #29 = Utf8               java/lang/System
  #30 = Utf8               out
  #31 = Utf8               Ljava/io/PrintStream;
  #32 = Utf8               java/io/PrintStream
  #33 = Utf8               println
  #34 = Utf8               (Ljava/lang/String;)V
{
  public LockSyncTest();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       5     0  this   LLockSyncTest;

  public static synchronized void m3();
    // ACC_STATIC, ACC_SYNCHRONIZED标识符
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;      
         3: ldc           #3                  // String 静态同步方法
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 6: 0
        line 7: 8

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       1     0  args   [Ljava/lang/String;
}

4 synchronized锁的是什么

Q:为什么任何一个对象都可以成为一个锁?
A:在HotSpot虚拟机中,monitor采用ObjectMonitor实现;

4.1 什么是管程?

管程也叫监视器(Monitors),是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。

这些共享资源一般是硬件设备或一群变量。对共享变量能够进行的所有操作集中在一个模块中。(把信号量及其操作原语“封装”在一个对象内部)管程实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。管程提供一种机制,管程可以看作一个软件模块,它是将共享的变量和对于这些共享变量的操作封装起来,形成一个具有一定接口的功能模块,进程可以调用管程来实现进程级别的并发控制


4.2 底层C++代码解读

ObjectMonitor.java -> ObjectMonitor.cpp ->ObjectMonitor.hpp

ObjectMonitor.hpp部分源码:

  ObjectMonitor() {
    _header       = NULL;
    _count        = 0;
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    _WaitSet      = NULL;
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ;
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
  }


ObjectMonitor中几个关键的属性:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/429034.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

水文水利数据对接详解

数据对接 水雨情监测及视频监控系统需要与什么平台进行对接&#xff1f; 答&#xff1a;水雨情监测及视频监控系统由省统一接收的方式&#xff0c;数据接收中心设在***水利云。 2.水雨情数据接收中心有哪些组成部分&#xff1f; 答&#xff1a;水雨情数据接收中心主要由硬件…

pdf如何压缩变小,pdf压缩教程四招快速学

PDF是我们日常工作中经常使用的文件格式之一。这种文件格式方便易用&#xff0c;能够确保文件在传输和接收过程中不会出现错版等问题。为了方便发送&#xff0c;我们通常会将编辑好的内容转换为PDF格式。但是有时候文件过大&#xff0c;无法通过传输渠道发送怎么办&#xff1f;…

字节5年测试工程师对“测试开发”的理解

写在前面&#xff1a; 写这篇文章的目的是为了能够更好的帮助刚入职的新人了解这个岗位和自己的工作&#xff0c;也想谈谈自己工作一年来对这个领域的了解程度&#xff0c;做一个小小总结吧&#xff5e; 一、我理解的测试开发 测试开发与开发、测试的关系 以前在没有接触测试…

樱花树盛开的季节,我用简单的C代码绘制了一棵樱花树向她表白~『C/C++图形库EasyX』

文章目录&#x1f490;专栏导读&#x1f490;文章导读绘制一根线条绘制一个简易的树干优化树干&#xff0c;使其更加细致绘制樱花树增加随机树形与渐变色效果如何设置随机数进阶——通过鼠标点击来控制生成樱花树进阶——生成樱花树并展示生长过程&#x1f490;专栏导读 &#…

通过阿里云函数计算解决ChatGPT API的调用问题

ChatGPT系列文章 与其被ChatGPT取代&#xff0c;不如征服ChatGPT&#xff0c;做它的主人&#xff01; 文章目录ChatGPT系列文章前言命令行部署准备工作两行命令实现部署应用中心部署使用代理访问API总结前言 自2022年11月30日 OpenAI 发布 ChatGPT 以来&#xff0c;虽然时有唱…

最新版本VSCode配置Python、PyQt5、QtDesigner环境并创建一个ui界面测试

参考链接&#xff1a;最新版本VSCode配置Python、PyQt5、QtDesigner环境并创建一个ui界面测试 一、安装Python3 PyQt5所支持的python版本是从3.5开始的&#xff0c;因此安装的Python3版本必须大于3.5。 我安装的位置是C:\Python\Python38。 参见真小白入门Pyhton的安装 二、安…

图-文多模态,大模型,预训练

参考老师的无敌课程 多模态任务是指需要同时处理两种或多种不同类型的数据&#xff08;如图像、文本、音频等&#xff09;的任务。例如&#xff0c;图像描述&#xff08;image captioning&#xff09;就是一种典型的多模态任务&#xff0c;它需要根据给定的图像生成相应的文本描…

XO08R2 1SBP260109R1001接地系统能够为dcs提供屏蔽层,消除电子噪声干扰

​ XO08R2 1SBP260109R1001接地系统能够为dcs提供屏蔽层&#xff0c;消除电子噪声干扰 dcs合理、可靠的系统接地&#xff0c;是dcs系统非常重要的内容。为了保证dcs系统的监测控制精度和安全、可靠运行&#xff0c;必须对系统接地方式、接地要求、信号屏蔽、接地线截面选择、接…

【C++学习】map和set的封装

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《C学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; map和set的封装&#x1f349;map和set中的红黑树&#x1f34c;set中的键值和map中的键值&#x1f349…

CTF杂项提纲

CTF的杂项是涉及编码&#xff0c;图片隐写&#xff0c;音频隐写&#xff0c;压缩包分析的方向&#xff0c;本文对MISC的知识点做了一个简单列举 常见编码 ASCII 0-9,48-57 A-Z 65-90 a-z 97-122 URL url编码又叫百分号编码&#xff0c;是统一资源定位的编码方式 base16/…

ModStartCMS v6.2.0 VIP权益配置功能,界面UI优化升级

ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用&#xff0c;支持后台一键快速安装&#xff0c;让开发者能快的实现业务功能开发。 系统完全开源&#xff0c;基于 Apache 2.0 开源协议&#xff0c;免费且不限制商业使用。 功能特性 丰富的模块市…

【初识数据库】数据库简介及MySQL安装

目录 数据库基本概念&#xff1a; 主流的关系型数据库&#xff1a; 下载并安装数据库 我们选择的是&#xff1a;MySQL Community Server 8.0.26 卸载老版本MySQL 数据库基本概念&#xff1a; 数据库&#xff1a;是数据的仓库&#xff0c;存储数据的地方 数据库管理系统&am…

RK3568平台开发系列讲解(调试篇)debugfs 分析手段

🚀返回专栏总目录 文章目录 一、enable debugfs二、debugfs API三、使用示例沉淀、分享、成长,让自己和他人都能有所收获!😄 📢Linux 上有一些典型的问题分析手段,从这些基本的分析方法入手,你可以一步步判断出问题根因。这些分析手段,可以简单地归纳为下图: 从这…

进程虚拟地址空间的划分

进程虚拟地址空间划分是操作系统中的一个核心概念&#xff0c;它决定了进程可以访问的内存范围和方式。在本文中&#xff0c;我们将介绍进程虚拟地址空间的划分方法和各个部分的作用。 进程虚拟地址空间的划分方法 在操作系统中&#xff0c;每个进程都有自己的虚拟地址空间&am…

到底还是留不住!库克“中国行”后火速变脸,3000亿产能转向印度?

以国家的层面来制衡一家科技企业&#xff0c;这种事或许只有老美干得出来。当看到华为即将崛起之时&#xff0c;美一意孤行&#xff0c;修改了大量关乎芯片和商贸的政策。层层围堵之下&#xff0c;华为如今的市场份额大幅缩水&#xff0c;腾出来的高端市场基本被苹果占据。相关…

2023年数据挖掘与知识发现国际会议(DMKD 2023) | IOP JPCS独立出版

会议简介 Brief Introduction 2023年数据挖掘与知识发现国际会议(DMKD 2023) 会议时间&#xff1a;2023年6月24日-26日 召开地点&#xff1a;中国重庆 大会官网&#xff1a;DMKD 2023-2023 International Conference on Data Mining and Knowledge Discovery 由重庆邮电大学、重…

找工作吗?50道Python面试题集锦【附答案】

嗨害大家好鸭&#xff01;我是爱摸鱼的芝士~ 希望能够帮助你在今年的求职面试中脱颖而出&#xff0c; 找到一份高薪工作~ 这些面试题涉及Python基础知识、Python编程、数据分析以及Python函数库等多个方面。 提前预祝给这篇文章点赞收藏的友友们~ 拿到心中最满意的那一份OF…

PHP快速入门13-MySQL数据库与Redis操作

文章目录前言一、PHP连接MySQL1.1 建立数据库链接1.2 插入数据1.3 更新数据1.4 删除数据1.5 查询数据并输出结果1.6 查询数据并返回结果1.7 获取查询结果的行数1.8 获取查询结果的列数1.9 获取查询结果的字段名1.10 获取查询结果的字段类型1.11 获取上一次操作影响的行数1.12 开…

PsExec流量分析

PsExec远程连接服务器 psexec是sysinternals提供的众多windows工具中的一个&#xff0c;这款工具的初衷是帮助管理员管理大量的机器的&#xff0c;后来被攻击者用来做横向渗透。 下载地址&#xff1a; https://docs.microsoft.com/en-us/sysinternals/downloads/psexec使用Ps…

d2l 里面GRU与Lstm实现

此二者的本质都是对rnn进行改良&#xff1a;关注当前多还是关注之前多。 在此详细讲一下。 目录 1.GRU门循环控制单元 1.1理论&#xff1a; 1.2初始化参数 1.3定义网络 1.4训练命令行 1.5简洁实现 2.Lstm长短期记忆网络 2.1理论 2.2加载参数 2.3定义lstm计算 2.4定义…