Java 并发编程(三)Synchronized底层优化(偏向锁与轻量级锁)

2024-04-25 23:48

本文主要是介绍Java 并发编程(三)Synchronized底层优化(偏向锁与轻量级锁),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Synchronized低效的原因

在Java SE 1.6发布前,使用Synchronized关键字实现同步功能是比较低效的,很多人称其为重量级锁.究其原理,是因为Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的,而监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。操作系统实现线程之间的切换需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低的原因。

Java SE 1.6为Synchronized带来的优化(偏向锁和轻量级锁)

Java SE 1.6中为了减少获得锁和释放锁带来的性能消耗,引入了偏向锁和轻量级锁.要想弄清楚这两个锁的原理首先要了解Java对象头,因为Synchronized用的锁就存在Java对象头中.

Java对象头

Java对象头的主要内容

内容说明
Mark Work存储对象的hashCode或锁信息等
Class Metadata Address存储到对象类型数据的地址(即指向该对象的类型数据的指针)
Array Length数组的长度(如果当前对象是数组)

32位的JVM的Mark Work的默认存储结构

锁状态25bit4bit1bit 是否为偏向锁2bit 锁标志位
无锁状态对象的hashCode对象分代年龄001

Mark Word存储结构中的数据会随着标志位的变化而变化.
Mark Word可能的状态变化
image
在Java SE 1.6中,锁一共有4中状态,级别从低到高依次为:

  • 无锁状态
  • 偏向锁
  • 轻量级锁
  • 重量级锁

值得注意的是锁状态只能升级不能降级,也就是说轻量级可以膨胀为重量级锁,但是这个过程不可以逆转.

1.偏向锁

为何引入偏向锁

大多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程多次获得.
引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令(由于一旦出现多线程竞争的情况就必须撤销偏向锁,所以偏向锁的撤销操作的性能损耗必须小于节省下来的CAS原子指令的性能消耗)
偏向锁是在只有一个线程执行同步块时进一步提高性能

偏向锁的获取过程:
  1. 访问对象头的Mark Word中的锁标志位是否为01(确认为可偏向状态)
  2. 如果为可偏向状态,则测试对象头中Mark Word的线程ID是否指向当前线程,如果是则进入步骤5,否则进入步骤3
  3. 如果对象头中Mark Word的线程ID并未指向当前线程,则当前线程通过CAS操作竞争锁.如果竞争成功,则将Mark Word中线程ID设置为当前ID,然后执行步骤5.如果竞争失败,则执行步骤4.
  4. 如果CAS竞争偏向锁失败,则表示有竞争.当到到全局安全点时获得偏向锁的线程被挂起,偏向锁升级为轻量级锁,然后被阻塞在安全点的线程继续往下执行同步代码.
  5. 执行同步代码

注意:第四步中到达安全点safepoint会导致stop the word,时间很短。

偏向锁的撤销

使用一种等到竞争出现才释放锁的机制,即当其他线程竞争偏向锁时,持有偏向锁的线程会运行到全局安全点后挂起,判断锁对象是否处于被锁定状态,撤销偏向锁后恢复到未锁定(标志位为“01”)或轻量级锁(标志位为“00”)的状态。

2.轻量级锁

为何引入轻量级锁

轻量级锁是为了在没有多线程竞争的前提下,减少传统的重量级锁使用产生的性能消耗。轻量级锁所适应的场景是线程交替执行同步块的情况,如果存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀为重量级锁。

轻量级锁的加锁过程:
  1. 在线程进入同步代码块时,如果同步对象锁状态为无锁状态(锁标志位为01,可否可偏向锁为0),JVM会首先在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,官方名称为’Displaced Mark Word’.这个时候线程堆栈与对象头的状态如图1所示.
  2. 拷贝对象头的Mark Word复制到当前线程栈帧的锁记录中.
  3. 拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向锁记录(Lock Record)的指针,并将锁记录(Lock Record)里的owner指针指向所对象的Mark Word.如果CAS更新操作成功,则执行步骤4,否则执行步骤5.
  4. 如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,即表示此对象处于轻量级锁定状态,这时候线程堆栈与对象头的状态如图2所示。
  5. 如果这个更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行。否则说明多个线程竞争锁,轻量级锁就要膨胀为重量级锁,锁标志的状态值变为“10”,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。 而当前线程便尝试使用自旋来获取锁.


轻量级锁的撤销
  1. 通过CAS操作尝试把线程中复制的Displaced Mark Word对象替换当前的Mark Word。
  2. 如果替换成功,整个同步过程就完成了。
  3. 如果替换失败,说明有其他线程尝试过获取该锁(此时锁已膨胀),那就要在释放锁的同时,唤醒被挂起的线程。

3.锁的优缺点对比

优点缺点适用场景
偏向锁加锁和解锁不需要额外的消耗,和执行非同步方法比时间相差无几如果线程存在锁竞争,会带来额外的锁撤销的消耗适用于只有一个线程访问同步块场景
轻量级锁竞争的线程不会阻塞,提高了程序响应速度如果始终得不到锁竞争的线程,使用自旋会消耗CPU追求响应速度,同步块执行速度非常快
重量级锁线程竞争不会自旋,不会消耗CPU线程阻塞,响应速度慢追求吞吐量,同步块执行速度慢或者执行时间长.

Java 并发编程(一)Volatile原理剖析及使用
Java 并发编程(二)Synchronized原理剖析及使用
Java 并发编程(三)Synchronized底层优化(偏向锁与轻量级锁)
Java 并发编程(四)JVM中锁的优化
Java 并发编程(五)原子操作类

这篇关于Java 并发编程(三)Synchronized底层优化(偏向锁与轻量级锁)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/936131

相关文章

SpringBoot项目中Maven剔除无用Jar引用的最佳实践

《SpringBoot项目中Maven剔除无用Jar引用的最佳实践》在SpringBoot项目开发中,Maven是最常用的构建工具之一,通过Maven,我们可以轻松地管理项目所需的依赖,而,... 目录1、引言2、Maven 依赖管理的基础概念2.1 什么是 Maven 依赖2.2 Maven 的依赖传递机

SpringBoot实现动态插拔的AOP的完整案例

《SpringBoot实现动态插拔的AOP的完整案例》在现代软件开发中,面向切面编程(AOP)是一种非常重要的技术,能够有效实现日志记录、安全控制、性能监控等横切关注点的分离,在传统的AOP实现中,切... 目录引言一、AOP 概述1.1 什么是 AOP1.2 AOP 的典型应用场景1.3 为什么需要动态插

Oracle查询优化之高效实现仅查询前10条记录的方法与实践

《Oracle查询优化之高效实现仅查询前10条记录的方法与实践》:本文主要介绍Oracle查询优化之高效实现仅查询前10条记录的相关资料,包括使用ROWNUM、ROW_NUMBER()函数、FET... 目录1. 使用 ROWNUM 查询2. 使用 ROW_NUMBER() 函数3. 使用 FETCH FI

Java实现Excel与HTML互转

《Java实现Excel与HTML互转》Excel是一种电子表格格式,而HTM则是一种用于创建网页的标记语言,虽然两者在用途上存在差异,但有时我们需要将数据从一种格式转换为另一种格式,下面我们就来看看... Excel是一种电子表格格式,广泛用于数据处理和分析,而HTM则是一种用于创建网页的标记语言。虽然两

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

Java访问修饰符public、private、protected及默认访问权限详解

《Java访问修饰符public、private、protected及默认访问权限详解》:本文主要介绍Java访问修饰符public、private、protected及默认访问权限的相关资料,每... 目录前言1. public 访问修饰符特点:示例:适用场景:2. private 访问修饰符特点:示例:

详解Java如何向http/https接口发出请求

《详解Java如何向http/https接口发出请求》这篇文章主要为大家详细介绍了Java如何实现向http/https接口发出请求,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 用Java发送web请求所用到的包都在java.net下,在具体使用时可以用如下代码,你可以把它封装成一

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

SpringBoot使用Apache Tika检测敏感信息

《SpringBoot使用ApacheTika检测敏感信息》ApacheTika是一个功能强大的内容分析工具,它能够从多种文件格式中提取文本、元数据以及其他结构化信息,下面我们来看看如何使用Ap... 目录Tika 主要特性1. 多格式支持2. 自动文件类型检测3. 文本和元数据提取4. 支持 OCR(光学