Java Synchronized是什么
Synchronized是什么
各位Java读者,对于synchronized关键字并不陌生,在各种中间件源码或者JDK源码中都能看到,对于不熟悉synchronized的读者只知道在多线程中需要使用到synchronized关键字,知道synchronized能够保证线程安全。
称之为:互斥锁(同时只能一个线程执行,其他的线程将会等待)
又称之为:悲观锁(同时只能一个线程执行,其他的线程将会等待)
JVM虚拟机帮你实现,开发者只需要使用synchronized关键字即可。
使用时需要用一个对象当锁的互斥量
能够保证一段代码(临界区)的原子性+可见性。
从字节码层面解析Synchronized关键字
从案例入手,最合适不过。
class Demo1{ // 互斥对象 static Object object = new Object(); // 竞争条件 static int cout = 0; public static void main(String[] args) { // 互斥 synchronized(object){ // 以下是临界区 cout++; System.out.println("synchronized"); } } }
仅仅从Java代码,我们并不能看出啥东西,而Java程序编译后是字节码文件,所以我们解析一遍字节码
Constant pool:
#1 = Methodref #7.#26 // java/lang/Object."":()V
#2 = Fieldref #8.#27 // Demo1.object:Ljava/lang/Object;
#3 = Fieldref #8.#28 // Demo1.cout:I
#4 = Fieldref #29.#30 // java/lang/System.out:Ljava/io/PrintStream;
#5 = String #31 // synchronized
#6 = Methodref #32.#33 // java/io/PrintStream.println:(Ljava/lang/String;)V
#7 = Class #34 // java/lang/Object
#8 = Class #35 // Demo1
#9 = Utf8 object
#10 = Utf8 Ljava/lang/Object;
#11 = Utf8 cout
#12 = Utf8 I
#13 = Utf8
#14 = Utf8 ()V
#15 = Utf8 Code
#16 = Utf8 LineNumberTable
#17 = Utf8 main
#18 = Utf8 ([Ljava/lang/String;)V
#19 = Utf8 StackMapTable
#20 = Class #36 // "[Ljava/lang/String;"
#21 = Class #34 // java/lang/Object
#22 = Class #37 // java/lang/Throwable
#23 = Utf8
#24 = Utf8 SourceFile
#25 = Utf8 Demo1.java
#26 = NameAndType #13:#14 // "":()V
#27 = NameAndType #9:#10 // object:Ljava/lang/Object;
#28 = NameAndType #11:#12 // cout:I
#29 = Class #38 // java/lang/System
#30 = NameAndType #39:#40 // out:Ljava/io/PrintStream;
#31 = Utf8 synchronized
#32 = Class #41 // java/io/PrintStream
#33 = NameAndType #42:#43 // println:(Ljava/lang/String;)V
#34 = Utf8 java/lang/Object
#35 = Utf8 Demo1
#36 = Utf8 [Ljava/lang/String;
#37 = Utf8 java/lang/Throwable
#38 = Utf8 java/lang/System
#39 = Utf8 out
#40 = Utf8 Ljava/io/PrintStream;
#41 = Utf8 java/io/PrintStream
#42 = Utf8 println
#43 = Utf8 (Ljava/lang/String;)V
0: getstatic #2 // 从2号常量池中拿到静态变量,压入到操作数栈中
3: dup // 把操作数栈栈顶的对象赋值一份
4: astore_1 // 将操作数栈的数据保存到1号局部变量表中,给释放锁使用
5: monitorenter // 互斥锁开启,也是synchronized的字节码层面实现
6: getstatic #3 // 从2号常量池中拿到静态变量,压入到操作数栈中
9: iconst_1 // 将常量1压入到操作数栈中
10: iadd // 消耗两个操作数栈的数据,相加,然后压入栈顶
11: putstatic #3 // 将操作数栈栈顶的变量赋值给3号常量池
14: getstatic #4 // 将4号常量池的对象压入操作数栈
17: ldc #5 // 解析5号常量池的符号,拿到字符串常量"synchronized"
19: invokevirtual #6 // 执行println函数,消耗2个操作数栈
22: aload_1 // 将1号局部变量表的数据压入操作数栈
23: monitorexit // 互斥锁的结束,也是synchronized的字节码层面实现
24: goto 32 // 跳转到32行。
27: astore_2 // 可能存在异常,但是要需要释放锁,所以把异常对象放入2号局部变量表
28: aload_1 // 把1号局部变量表数据压入操作数栈的栈顶,供monitorexit指令使用
29: monitorexit // 可能存在异常,但是要需要释放锁,不然死锁了。
30: aload_2 // 把异常对象从2号局部变量表中压入操作数栈的栈顶
31: athrow // 存在异常抛出
32: return // 函数返回
以上是字节码全解,其实很简单,最终Synchronized关键字解析成字节为monitorenter和monitorexit字节码指令,然后每次执行这2个字节码指令前,把互斥对象压入操作数栈供给monitorenter和monitorexit字节码指令使用。
所以下一篇就是去Hotspot源码中解析monitorenter和monitorexit字节码指令的详细流程。
Synchronized与ReentrantLock的区别
这是一道很常见的面试题,面试被问到的频率非常高
相似点:
都是互斥锁的实现
不同点:
Synchronized基于JVM内部实现,ReentrantLock基于Java层面实现(但是ReentrantLock核心代码还是调用C++代码)。
Synchronized在1.6以后经过优化,存在几个不同级别的锁,根据线程竞争的力度提升锁的力度(俗称锁升级),更多的适合场景,而ReentrantLock在锁力度选择上略显死板。
ReentrantLock虽然在锁力度选择上略显死板,但是可以选择公平和非公平,而Synchronized只能是非公平锁
ReentrantLock的条件等待队列,可创建多个,高定制化。而Synchronized底层只有一个队列。
ReentrantLock需要用户手动开启锁,手动释放锁。而Synchronized关键字底层通过字节码自动实现
以上是Java Synchronized是什么的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题











Java 8引入了Stream API,提供了一种强大且表达力丰富的处理数据集合的方式。然而,使用Stream时,一个常见问题是:如何从forEach操作中中断或返回? 传统循环允许提前中断或返回,但Stream的forEach方法并不直接支持这种方式。本文将解释原因,并探讨在Stream处理系统中实现提前终止的替代方法。 延伸阅读: Java Stream API改进 理解Stream forEach forEach方法是一个终端操作,它对Stream中的每个元素执行一个操作。它的设计意图是处

胶囊是一种三维几何图形,由一个圆柱体和两端各一个半球体组成。胶囊的体积可以通过将圆柱体的体积和两端半球体的体积相加来计算。本教程将讨论如何使用不同的方法在Java中计算给定胶囊的体积。 胶囊体积公式 胶囊体积的公式如下: 胶囊体积 = 圆柱体体积 两个半球体体积 其中, r: 半球体的半径。 h: 圆柱体的高度(不包括半球体)。 例子 1 输入 半径 = 5 单位 高度 = 10 单位 输出 体积 = 1570.8 立方单位 解释 使用公式计算体积: 体积 = π × r2 × h (4

Java是热门编程语言,适合初学者和经验丰富的开发者学习。本教程从基础概念出发,逐步深入讲解高级主题。安装Java开发工具包后,可通过创建简单的“Hello,World!”程序实践编程。理解代码后,使用命令提示符编译并运行程序,控制台上将输出“Hello,World!”。学习Java开启了编程之旅,随着掌握程度加深,可创建更复杂的应用程序。
