java 同步锁:synchronized 关键字

简介

synchronized关键字是Java里面最基本的同步手段,它经过编译之后,会在同步块的前后分别生成 monitorentermonitorexit字节码指令,这两个字节码指令都需要一个引用类型的参数来指明要锁定和解锁的对象;而直接使用 synchronized 关键字锁定方法时,生成的字节码指令里面并没有 monitorenter 和 monitorexit 这两个指令,而是为方法添加了一个flags: ACC_SYNCHRONIZED, 该标识指明了该方法是一个同步方法。

synchronized 的两种不同用法

一、作用于方法上

public synchronized void method1(){ System.out.println(1); }

查看编译后的字节码,发现会在方法的中加入ACC_SYNCHRONIZED的标识:

public synchronized void method1(); descriptor: ()V flags: (0x0021) ACC_PUBLIC, **ACC_SYNCHRONIZED** Code: stack=2, locals=1, args_size=1

二、作用于代码块

public void method3(){ synchronized(this){ System.out.println(3); } }

查看编译后的字节码,发现会在同步块的前后分别生成 monitorentermonitorexit字节码指令

public void method3(); descriptor: ()V flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: aload_0 1: dup 2: astore_1 3: **monitorenter** 4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 7: iconst_3 8: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 11: aload_1 12: **monitorexit** ... 省略...

实现原理

在学习Java内存模型内存间的交互操作的时候,知道JAVA内存模型还提供了更大范围的原子性保证 :lockunlock操作。

但是lock和 unlock操作并 没有直接提供给用户使用,而是提供了更高层次的字节码指令 monitorentermonitorexit来隐式的使用lock和 unlock操作。

Lock(锁定)命令:把一个变量标识为一条线程独占的状态,作用于主内存的变量

unlock(解锁)命令:把处于锁定状态的变量释放锁,缩放锁后其他线程才可以锁定,作用于主内存的变量

而 synchronized 就是使用 monitorenter 和 monitorexit 这两个指令来实现锁的功能的。

根据JVM规范的要求,在执行monitorenter指令的时候,首先要去尝试获取对象的锁,如果这个对象没有被锁定,或者当前线程已经拥有了这个对象的锁,就把锁的计数器加1,相应地,在执行monitorexit的时候会把计数器减1,当计数器减小为0时,锁就释放了。所以 synchronized 对同一条线程来说是一个可重入的。

synchronized 同步块在已进入的线程执行完成之前,会阻塞后面的线程进入,而JAVA的线程是映射到操作系统的原生线程之上的,如果要阻塞或唤醒一个线程,都需要操作系统来帮忙完成,这就需要从用户态切换到内核态中,状态转换需要消耗很多的处理器时间,对与简单的同步块,状态转换消耗的时候可能比用户代码执行的时间还要长,所以 synchronized 是JAVA 语言中一个重量级的操作。

优化

Java中的锁优化

总结

(1)synchronized在编译时会在同步块前后生成monitorenter和monitorexit字节码指令或者ACC_SYNCHRONIZED的标识;

(2)monitorenter和monitorexit字节码指令需要一个引用类型的参数,基本类型不可以哦;

(3)monitorenter和monitorexit字节码指令更底层是使用Java内存模型的lock和unlock指令;

(4)synchronized是可重入锁;

(5)synchronized是非公平锁;

(6)synchronized可以同时保证原子性、可见性、有序性;

(7)synchronized有三种状态:偏向锁、轻量级锁、重量级锁;


__EOF__

本文作者精灵王
本文链接https://www.cnblogs.com/admol/p/13994297.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Admol  阅读(524)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
点击右上角即可分享
微信分享提示