synchronized原理

简介: synchronized原理

基本特点

结合之前总结的锁策略,我们就可以总结出,synchronized具有以下特性(jdk1.8):

1.开始时是乐观锁,如果锁冲突频繁,就转换为悲观锁.

2.开始是轻量级实现,如果锁被持有的时间较长,就转换为重量级锁.

3.实现轻量级锁的时候大概率用到自旋锁策略

4.是一种不公平锁

5.是一种可重入锁

6.不是读写锁.

加锁加工过程

JVM将synchronized锁分为无锁,偏向锁,轻量级锁,重量级锁状态.会根据情况,依次进行升级.

注意基本点:锁只能升级,不能降级,非必要不加锁.

偏向锁

第一个尝试加锁的线程,优先进入偏向锁状态.

偏向锁并不是真的"加锁",只是给对象头做一个"偏向锁的标记",记录这个锁属于哪个线程.如果后续没有其它线程来竞争该锁,那么就不用进行其他同步操作了(避免了加锁解锁的开销).如果后续有其它线程来竞争该锁(刚才已经在锁对象中记录了当前锁属于哪个线程了,很容易识别当前申请锁的线程是不是之前记录的线程),那就取消原来偏向锁的状态,进入一般轻量级锁的状态,进入一般轻量级锁的状态.

偏向锁本质上相当于"延迟加锁",能不加锁就不加锁,尽量来避免不必要的加锁开销.

但是该做的标记还是得做的,否则无法区分何时需要真正加锁.

轻量级

随着其它线程进入竞争,偏向锁状态被消除,进入轻量级锁的状态(自适应的自旋锁).

此处的轻量级锁通过CAS实现(认识过程即可,CAS后面会讲)

1.通过CAS检查并更新一块内存(比如null->该线程引用)

2.如果更新成功,则认为加锁成功

3.如果更新失败,则认为锁被占用,继续自旋式地等待(并不放弃CPU).

自旋操作是一直让CPU空转,比较浪费CPU资源.

因此此处的自旋不会一直持续进行,而是达到了一定的时间/重试次数,就不再自旋了.

也就是所谓的"自适应".

于此同时synchronized内部也会统计当前锁对象上,有多少线程参与竞争,如果整体CPU消耗大,则会转为重量级锁.

重量级锁

如果竞争进一步激烈,自旋不能快速获取到锁状态,就会膨胀为重量级锁.

此处的重量级锁就是指用到内核提供的mutex.

1.执行加锁操作,先进入内核态

2.在内核态判定当前锁是否已经被占用

3.如果该锁没有占用,则加锁成功,并切换回用户态.

4.如果该锁被占用,则加锁失败.此时线程进入锁的等待队列,挂起.等待被操作系统唤醒.

5.经历了一系列的沧海桑田,这个锁已经被线程释放了,操作系统也想起了这个挂起的线程,于是唤醒了这个线程,尝试重新获取到锁.

其它的优化操作

锁消除

也是synchronized中内置的优化策略,代码不需要加锁就会删除.

编译器+JVM判断锁是否可以消除,如果可以,就直接消除.

什么是"锁消除"

编译器优化的一种方式,编译器编译代码时,如发现该代码不需要加锁,就会把锁自动干掉.

有些应用程序的代码中,用到了synchronized,但其实没有在多线程环境下.(例如StringBuffer)

StringBuffer sb = new StringBuffer();
sb.append("a");
sb.append("b");
sb.append("c");

此时每个append的调用都会涉及到加锁和解锁.但如果只是在单线程中执行这个代码,那么这些加锁解锁是没必要的,白白浪费了一些资源开销.

锁粗化

一段逻辑中如果多次出现加锁解锁,编译器+JVM会自动进行锁的粗化.

即讲多个细粒度的锁,合并成一个粗粒度的锁.(代码越少越细,越多越粗).

锁的粒度:粗和细

通常情况下,其实是更偏好让锁的粒度更细一些,更有利于多个线程并发执行的.

但是实际上可能并没有其它线程来抢占这个锁.这种情况JVM就会自动把锁粗化,避免频繁申请释放锁.

举个例子理解锁粗化:

领导给下属交代工作两种方式:

方式1:

打电话,交代任务1,挂电话.

打电话,交代任务2,挂电话.

打电话,交代任务3,挂电话.

方式2:

打电话,交代任务1,交代任务2,交代任务3,挂电话.

显然方式2更为高效.

相关面试题

什么是偏向锁?

偏向锁并不是真的加锁,而只是在锁的对象头中记录一个标记(记录该锁所属的线程).如果没有其它线程参与竞争锁,那么就不会真正执行加锁操作,从而降低程序开销.一旦真的涉及到其它的线程竞争,再取消偏向锁的状态,进入轻量级锁的状态.

相关文章
|
Kubernetes 负载均衡 应用服务中间件
【K8S系列】第十三讲:Ingress详解
【K8S系列】第十三讲:Ingress详解
8164 0
|
8月前
|
存储 算法 Java
G1原理—5.G1垃圾回收过程之Mixed GC
本文介绍了G1的Mixed GC垃圾回收过程,包括并发标记算法详解、三色标记法如何解决错标漏标问题、SATB如何解决错标漏标问题、Mixed GC的过程、选择CollectSet的算法
G1原理—5.G1垃圾回收过程之Mixed GC
|
6月前
|
Oracle Java 关系型数据库
JVM深入原理(一+二):JVM概述和JVM功能
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行。
183 0
|
6月前
|
算法 Java 对象存储
JVM深入原理(八)(二):垃圾回收
Java垃圾回收过程会通过单独的GC线程来完成,但是不管使用哪一种GC算法,都会有部分阶段需要停止所有的用户线程。这个过程被称之为StopTheWorld简称STW,如果STW时间过长则会影响用户的使用。一般来说,堆内存越大,最大STW就越长,想减少最大STW,就会减少吞吐量,不同的GC算法适用于不同的场景。分代回收算法将整个堆中的区域划分为新生代和老年代。--超过新生代大小的大对象会直接晋升到老年代。
158 0
|
存储 人工智能 文字识别
利用AI能力平台实现档案馆纸质文件的智能化数字处理
在传统档案馆中,纸质文件管理面临诸多挑战。AI能力平台利用OCR技术,通过图像扫描、预处理、边界检测、文字与图片分离、文字识别及结果存储等步骤,实现高效数字化转型,大幅提升档案处理效率和准确性。
420 7
|
存储 Java C++
Synchronized底层原理
Synchronized底层原理
221 2
|
存储 缓存 安全
大厂面试高频:ConcurrentHashMap 的实现原理( 超详细 )
本文详细解析ConcurrentHashMap的实现原理,大厂高频面试,必知必备。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:ConcurrentHashMap 的实现原理( 超详细 )
|
NoSQL Linux
看懂GDB调试核心:剖析ptrace原理及其应用场景!(上)
看懂GDB调试核心:剖析ptrace原理及其应用场景!
|
XML 缓存 SpringCloudAlibaba
Spring循环依赖解决方案
循环依赖其实就是一个闭环,像图中情况二Spring在创建单例bean A的时候发现引用了B,这时候就会去容器中查找单例bean B,发现没有然后就会创建bean B,创建bean B时又发现引用了bean A,这时候又会去容器中查找bean A,发现没有,接下来就会循环重复上面的步骤,这是不是像极了死锁?其实循环依赖就是一个死循环的过程
319 0
Spring循环依赖解决方案
|
传感器
基于51单片机的车辆倒车雷达报警系统
该文描述了一个基于51单片机的超声波倒车雷达系统设计,要求包括:2cm至4m的测量范围,3mm精度,集成DS18B20温度传感器以校准声速,使用LCD1602显示距离和温度,具备按键设置预警距离及蜂鸣器报警功能。系统由AT89C51单片机、HC-SR04超声波模块、DS18B20温度模块、报警电路和LCD显示电路组成。文中还展示了Proteus仿真电路图和部分仿真结果分析,包括LCD显示示例和预警距离设置操作。
365 4