Java并发编程 | Synchronized原理与使用

2022/12/6 1:23:59

本文主要是介绍Java并发编程 | Synchronized原理与使用,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Java提供了多种机制实现多线程之间有需要同步执行的场景需求。其中最基本的是Synchronized ,实现上使用对象监视器( Monitor )。

Java中的每个对象都是与线程可以锁定或解锁的对象监视器( Monitor )关联。在同一时间只有一个线程可以在对象监视器( Monitor )上保持锁定。任何其他线程试图锁定对象监视器( Monitor )都会被阻止,直到它们可以获得该监视器上的锁定。

Synchronized 基本使用方式

Synchronized 的作用范围,依据锁定的对象(object、this、class)、使用方式,可以分成五种情况。如果按照JVM字节码的区别,也可以分成两种形式:代码块(monitorenter、monitorexit)、函数(ACC_SYNCHRONIZED)。

虽然可以按照不同维度来划分 Synchronized 但本身机制是一样的,无论是 Synchronized 函数/代码块,都是通过对象监视器( Monitor )来实现。无论是this、class、object本质上都是一个对象,区别无非代表的是当前实例、类、一般实例,它们都有着对象监视器( Monitor )。

在HotSpot虚拟机中,对象监视器( Monitor ) 具体的实现类就是 ObjectMonitor(C++)。

在使用/分析 Synchronized 同步是否有效正确的时候,只需要分析需要的同步块是否作用在同一个对象监视器( Monitor )上。换一种描述,是否作用在同一个对象(Object)上,这里(Object)可以是this、object、class。

下面分别按照Synchronized 代码块、Synchronized 函数维度来进行详细介绍。

Synchronized 代码块

Synchronized 代码块的一般使用形式:synchronized ( Expression ) Block 。

Expression 必须是一个对象,可以是class、this、object,不能是原始类型(int、float...);否则编译的时候就会报错。如果 Expression 是null,会抛出NullPointerException 的异常。

Block表示一段逻辑代码,执行逻辑代码前会锁定Expression 的对象监视器( Monitor )。如果正常运行完成后,对象监视器( Monitor )会被释放;如果运行期间异常/中断了同样的也会释放对象监视器( Monitor )。先加锁确保其他线程无法进入执行,所以Synchronized 是悲观锁,JVM指令上使用monitorenter、monitorexit 来进行相关实现。

image

在字节码指令里可以也可以看到有两个monitorexit ,一个是正常运行后的释放;另一个是在异常(athrow)抛出前的释放。同一个线程可以多次进入被锁定的相同对象监视器( Monitor ),所以Synchronized 是可重入锁。

image

Synchronized 函数

Synchronized函数在同步原理上同 Synchronized代码块是没有区别的,都是通过锁定对象监视器( Monitor );区别在于这里的对象是隐藏了起来。同样的支持可重入。

如果是静态方法(static),锁定的对象是这个方法所在的class object 对象。
如果是普通的方法,锁定的是this(当前实例)对象。

编译成JVM字节码的时候,函数描述上会标识ACC_SYNCHRONIZED ,并不会在函数代码块中显示的使用monitorenter、monitorexit指令。

在调用函数前锁定对象监视器( Monitor ),完成运行后释放对象监视器( Monitor )。无论函数是否有显性的抛出/处理异常,如果有异常中断抛出前也会自动的释放锁定的对象监视器( Monitor )。

Synchronized 实现原理

Synchronized同步一直也在进行优化,也是跟随着JDK新理念一起发展。比如偏向锁、轻量锁、重量锁、适应性自旋等等机制。不同的JDK版本,不同的JVM可能都有所不同。

在HotSpot虚拟机中,抛开锁升级、自适应等机制;基本原理是线程通过 CAS抢占对象监视器( Monitor ) _Owner来实现锁,没有抢占的会进入 _EntryList 来进行放置。当然, 线程执行/中断释放_Owner后,_EntryList并不是简单按照FIFO来进行选择执行不会保证公平性,所以Synchronized是非公平锁。

图中_WaitSet没有体现用途,但其是很重要的一个结构, 用于当 _Owner 执行线程中断时,线程将会写入。值得注意获取到锁之后才能中断,等待锁时不可中断。当相关线程被唤醒后,会采有不同的策略重新回到_EntryList 或者 参与CAS竞争 _Owner,这里存在线程上下文切换的可能。

标签:JavaScript,函数,编程语言,Object,编程,Node, function 来源:

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。



这篇关于Java并发编程 | Synchronized原理与使用的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程