Synchronized 优化

目录

前言

重点

一、 轻量级锁

二、锁膨胀 

三、重量锁

四、偏向锁

五、其他优化

我的其他博客


前言

Java synchronized 是一种机制,可以保证多个线程在访问共享资源时的同步性。synchronized 关键字可以用于方法或代码块上,当一个线程获取了这个对象的锁后,其他线程如果也要获取这个锁的话就必须等待该线程释放锁。这样可以保证共享资源的访问顺序和正确性,避免数据竞争和死锁等问题。

重点

 

同步的重点在于确保多线程环境下的数据一致性和程序正确性。当多个线程同时访问共享资源时,如果没有适当的同步机制,可能会导致数据损坏、不一致的结果以及其他并发问题。以下是同步的重点:

  1. 互斥访问: 同步的主要目标是防止多个线程同时访问共享资源,从而避免数据损坏。通过使用锁机制,只有一个线程能够获得对资源的访问权,其他线程必须等待。

  2. 防止竞态条件: 竞态条件是指多个线程竞争同时修改共享数据的情况,可能导致不可预测的结果。同步确保在任何时刻只有一个线程能够修改共享资源,从而避免竞态条件。

  3. 确保原子性操作: 在某些情况下,需要确保某个操作是原子性的,即不可分割的。同步可以用来保证这些操作的原子性,以防止其他线程在操作进行过程中对其进行干扰。

  4. 保护临界区: 临界区是指包含对共享资源进行访问或修改的代码块。同步确保在任何时刻只有一个线程能够进入临界区,防止多个线程同时执行可能引起问题的代码。

  5. 避免死锁: 同步的设计需要注意避免死锁,即多个线程因为互相等待对方释放资源而无法继续执行的情况。合理的同步设计和锁的使用是避免死锁的关键。

一、 轻量级锁

轻量级锁是为了解决在多线程环境中对同一数据进行并发访问时的性能问题而设计的一种锁优化机制。它主要针对的是线程交替执行同步块的场景,而不是真正的多线程竞争。轻量级锁的设计思想是尽量减小锁的操作对性能的影响,避免使用传统的重量级锁(如synchronized)时引入的较大性能开销。

轻量级锁的基本思路是,在没有线程竞争的情况下,通过将对象头中的一部分空间作为锁存储线程ID,从而避免了传统锁中使用互斥量(mutex)的开销。如果多个线程同时尝试获取锁,会升级为重量级锁,以保证数据的正确性。

以下是一个简单的Java代码示例,演示了轻量级锁的使用:

public class LightWeightLockExample {private static class Counter {private int count = 0;public void increment() {synchronized (this) { // 这里的锁是轻量级锁count++;}}public int getCount() {synchronized (this) {return count;}}}public static void main(String[] args) throws InterruptedException {final Counter counter = new Counter();// 创建两个线程,分别对计数器进行操作Thread thread1 = new Thread(() -> {for (int i = 0; i < 100000; i++) {counter.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 100000; i++) {counter.increment();}});// 启动线程thread1.start();thread2.start();// 等待两个线程执行完毕thread1.join();thread2.join();// 输出最终计数结果System.out.println("Final Count: " + counter.getCount());}
}

 

在上述代码中,Counter类包含了一个计数器变量 count,并提供了两个方法 incrementgetCountincrement 方法在对 count 进行操作时使用了 synchronized 关键字,这意味着它使用了轻量级锁。两个线程分别调用 increment 方法对计数器进行增加,最终输出计数结果。

需要注意的是,轻量级锁的具体实现是依赖于具体的JVM实现的。在不同的JVM中,轻量级锁的表现可能有所不同,但它的基本思想是一致的。

二、锁膨胀 

锁膨胀是指在多线程环境下,一开始使用了较轻量的锁,但随着线程竞争的增加,锁会逐渐升级为更重量级的锁。锁膨胀的目的是在高并发的情况下更好地平衡性能和线程安全。Java中的锁膨胀通常表现为从偏向锁到轻量级锁,再到重量级锁的升级过程。

以下是锁膨胀的一般过程:

  1. 偏向锁(Biased Locking): 当对象被创建时,它的锁状态为偏向锁。此时假定只有一个线程会访问该对象,因此将该线程的ID记录在对象头中,这样在未发生竞争时,线程访问对象时无需进行同步操作。偏向锁适用于只有一个线程访问对象的情况。

  2. 轻量级锁(Lightweight Locking): 当有多个线程尝试获取同一对象的锁时,偏向锁会升级为轻量级锁。轻量级锁使用CAS(Compare and Swap)操作来尝试获取锁,避免了传统的互斥量的开销。如果CAS操作失败,表示存在竞争,则升级为重量级锁。

  3. 重量级锁(Heavyweight Locking): 当轻量级锁无法满足多线程的并发需求时,锁会进一步升级为重量级锁。重量级锁使用操作系统提供的互斥量机制,确保在任何时刻只有一个线程能够获取锁,从而保证数据的正确性。

以下是一个简单的Java代码示例,演示了锁膨胀的过程:

public class LockEscalationExample {private static class Counter {private int count = 0;public synchronized void increment() { // 初始为偏向锁,然后升级为轻量级锁,最后可能升级为重量级锁count++;}public int getCount() {synchronized (this) {return count;}}}public static void main(String[] args) throws InterruptedException {final Counter counter = new Counter();// 创建两个线程,分别对计数器进行操作Thread thread1 = new Thread(() -> {for (int i = 0; i < 100000; i++) {counter.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 100000; i++) {counter.increment();}});// 启动线程thread1.start();thread2.start();// 等待两个线程执行完毕thread1.join();thread2.join();// 输出最终计数结果System.out.println("Final Count: " + counter.getCount());}
}

在这个例子中,Counter类的 increment 方法使用了 synchronized 关键字,最初是偏向锁。但由于多个线程并发访问,最终可能升级为轻量级锁,甚至进一步升级为重量级锁,以确保数据的一致性。需要注意的是,锁膨胀的过程是由JVM自动管理的,程序员不需要手动介入。

三、重量锁

重量级锁(Heavyweight Lock)是一种用于多线程同步的锁机制,通常是通过互斥量(mutex)来实现。当多个线程竞争同一资源时,重量级锁能够确保在任何时刻只有一个线程能够访问共享资源,从而避免数据的不一致性和竞态条件。

重量级锁的主要特点包括:

  1. 互斥量: 重量级锁通常使用操作系统提供的互斥量机制,这是一种在操作系统层面上实现的同步机制。在Java中,synchronized 关键字就是使用重量级锁来实现同步的一种方式。

  2. 阻塞等待: 当一个线程获得了重量级锁后,其他线程如果尝试获得相同的锁,它们会被阻塞,直到持有锁的线程释放锁。这种阻塞等待的机制确保了对共享资源的互斥访问。

  3. 高开销: 由于涉及到操作系统层面的互斥量操作,重量级锁的开销相对较高。这包括线程的上下文切换、内核态和用户态的切换等。

下面是一个简单的Java代码示例,演示了重量级锁的使用:

public class HeavyweightLockExample {private static class Counter {private int count = 0;public synchronized void increment() { // 使用synchronized关键字,引入重量级锁count++;}public int getCount() {synchronized (this) {return count;}}}public static void main(String[] args) throws InterruptedException {final Counter counter = new Counter();// 创建两个线程,分别对计数器进行操作Thread thread1 = new Thread(() -> {for (int i = 0; i < 100000; i++) {counter.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 100000; i++) {counter.increment();}});// 启动线程thread1.start();thread2.start();// 等待两个线程执行完毕thread1.join();thread2.join();// 输出最终计数结果System.out.println("Final Count: " + counter.getCount());}
}

 在这个例子中,Counter类的 increment 方法和 getCount 方法都使用了 synchronized 关键字,这意味着它们都会引入重量级锁,以确保对计数器的操作是线程安全的。这样,每次只有一个线程能够执行 incrementgetCount 中的同步代码块,确保了对计数器的互斥访问。

四、偏向锁

偏向锁是Java中用于提高单线程访问同步块性能的锁优化机制。它假设在大多数情况下,锁是由同一个线程多次获得的,因此可以为第一次获取锁的线程提供一种特殊的优化。

以下是偏向锁的主要特点:

  1. 偏向线程: 当一个线程第一次进入同步块时,偏向锁会记录该线程的ID,并将对象头中的偏向线程字段设为该线程的ID。此时这个线程处于偏向模式。

  2. 无竞争情况下的快速获取: 在偏向模式下,当同一个线程再次进入同步块时,不需要进行任何额外的同步操作,因为偏向线程已经被记录。这使得同一个线程多次获得锁的情况下,不会引入额外的性能开销。

  3. 竞争时的撤销: 当有其他线程尝试获取同一把锁时,偏向锁就不能满足“偏向”了。此时,会撤销偏向模式,升级为轻量级锁。这个过程会涉及到CAS(Compare and Swap)操作,确保多个线程之间的竞争是公平的。

  4. 减小锁撤销的概率: 为了减小锁撤销的概率,JVM会在撤销偏向锁时判断是否有其他线程访问过这个对象。如果没有,就直接将对象头的偏向线程字段置为0,不进行锁升级,以避免不必要的开销。

以下是一个简单的Java代码示例,演示了偏向锁的使用:

public class BiasedLockExample {private static class Counter {private int count = 0;public synchronized void increment() { // 初始为偏向锁count++;}public int getCount() {synchronized (this) {return count;}}}public static void main(String[] args) throws InterruptedException {final Counter counter = new Counter();// 创建两个线程,分别对计数器进行操作Thread thread1 = new Thread(() -> {for (int i = 0; i < 100000; i++) {counter.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 100000; i++) {counter.increment();}});// 启动线程thread1.start();thread2.start();// 等待两个线程执行完毕thread1.join();thread2.join();// 输出最终计数结果System.out.println("Final Count: " + counter.getCount());}
}

在这个例子中,Counter类的 increment 方法和 getCount 方法都使用了 synchronized 关键字,最初是偏向锁。由于在这个例子中只有一个线程在访问同步块,因此偏向锁会提高性能,避免了多线程竞争的开销。

五、其他优化

除了偏向锁之外,Java虚拟机还有其他一些锁优化策略和技术,以提高多线程程序的性能。以下是一些常见的锁优化技术:

  1. 轻量级锁(Lightweight Lock): 在多线程并发访问时,轻量级锁通过CAS(Compare and Swap)操作来避免传统锁的互斥开销,以提高性能。如果轻量级锁获取失败,会升级为重量级锁。

  2. 自旋锁(Spin Lock): 自旋锁是一种避免线程阻塞的锁机制。当一个线程尝试获取锁时,如果锁已经被其他线程占用,该线程不会立即阻塞,而是会进行一定次数的自旋等待,期望其他线程能够尽快释放锁。

  3. 适应性自旋锁: 为了更好地适应不同的运行环境,一些虚拟机实现引入了适应性自旋锁,它可以动态地调整自旋等待的次数,以根据当前运行环境的负载情况来提高性能。

  4. 锁消除: 编译器对代码进行优化时,可以通过静态分析来判断某些锁不会被多个线程同时访问,从而将锁操作直接消除,减少同步的开销。

  5. 锁粗化: 锁粗化是将多个连续的同步操作合并为一个大的同步块,以减少同步的次数,提高性能。这样可以避免频繁地进入和退出同步块造成的开销。

  6. 分段锁: 在一些数据结构中,可以使用分段锁来代替全局锁,将数据分成多个段,每个段独立加锁,从而提高并发度,减小锁的争用。

这些优化技术并非独立的,往往在实际应用中会综合使用,根据具体的应用场景和性能需求选择合适的锁策略。锁优化是多线程编程中的一个重要方面,通过有效地使用这些技术,可以提高程序的并发性能。需要注意的是,过度的锁优化可能导致复杂性增加,因此在选择和应用锁优化技术时需要谨慎权衡。

我的其他博客

简单介绍一些其他的树-CSDN博客

认识二叉树(详细介绍)-CSDN博客

正则表达式详细讲解-CSDN博客

低代码开发:创新之道还是软件开发的捷径?-CSDN博客

HTTP与HTTTPS的区别-CSDN博客

什么情况下会产生StackOverflowError(栈溢出)和OutOfMemoryError(堆溢出)怎么排查-CSDN博客

在多线程中sleep()和wait()的区别(详细)-CSDN博客

谈谈我对HashMap扩容机制的理解及底层实现-CSDN博客

堆排序详细讲解(一文足矣JAVA)-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/216568.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

消息中间件比较

那都有哪些中间件可供选择呢。其实现在主流的消息中间件就4种&#xff1a;kafka、ActiveMQ、RocketMQ、RabbitMQ 下面我们来看一下&#xff0c;他们之间有什么区别&#xff0c;他们分别应该用于什么场景 ActiveMQ 我们先看ActiveMQ。其实一般早些的项目需要引入消息中间件&…

Java笔记草稿——已完成

导航&#xff1a; 【Java笔记踩坑汇总】Java基础JavaWebSSMSpringBootSpringCloud瑞吉外卖/黑马旅游/谷粒商城/学成在线设计模式面试题汇总性能调优/架构设计源码-CSDN博客 推荐学习视频&#xff1a; 黑马程序员全套Java教程_哔哩哔哩 尚硅谷Java入门视频教程_哔哩哔哩 目录 零…

Unity_ET框架项目-斗地主_启动运行流程

unity_ET框架项目-斗地主_启动运行流程 项目源码地址&#xff1a; Viagi/LandlordsCore: ET斗地主Demohttps://github.com/Viagi/LandlordsCore下载项目到本地。 启动运行步骤&#xff1a; 下载目录如下&#xff1a; 1. VS&#xff08;我用是2022版VisualStudio&#xff09…

快速上手linux | 一文秒懂Linux各种常用目录命令(上)

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 一 、命令提示符和命令的基本格式1.1 如何查看主机名称及修改 二、命令基本格式2.1 命令格式示例2.2 参数的作用…

C# WebSocket简单使用

文章目录 前言Fleck调试工具初始化简单使用 前言 最近接到了一个需求&#xff0c;需要网页实现上位机的功能。那就对数据传输的实时性要求很高。那就只能用WebSocket了。这里简单说一下我的WebSocket如何搭建 Fleck C# WebSocket(Fleck) 客户端:html Winfrom Fleck Github官网…

Kafka集成springboot

安装kafka&#xff0c;直接到官网下载bin文件&#xff0c;本文使用windows进行使用kafka。 下载之后&#xff0c;第一步&#xff0c;启动zookeeper&#xff1a; zookeeper-server-start.bat ..\..\config\zookeeper.properties 第二步&#xff0c;启动kafka&#xff1a; kafka…

对比SPI、UART、I2C通信的区别与应用

SPI、UART、I2C通信是常用的数字通信协议&#xff0c;它们在不同的场景下有不同的应用。下面&#xff0c;我将分别介绍它们的特点、区别与应用。 SPI通信 SPI通信是一种串行同步通信协议&#xff0c;它的全称为“Serial Peripheral Interface”。SPI通信是一种单主多从的通信方…

你都那么老了,还在每天写博客吗?

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 白色便民网&#xff1a;我想多开一个公司会不会被税局查? 事件背景&#xff1a; 松松已创业9年&#xff0c;自媒体14年&#xff0c;经历过从0开公司、项目失败、赚钱等各种高光时刻。所以对于小微企业经营还是…

程序中关于时间和比较运算符的单词

时间 在日志中&#xff0c;我们经常碰到关于一些时间的单词缩写 比如这个Fri Dec 1 就代表了Friday &#xff08;星期五&#xff09;&#xff0c; December &#xff08;十二月&#xff09; 12月1日星期五 或者使用date查看时间的时候 dateWed Dec 13 05:55:54 PM CST 2023这…

OfficeWeb365 SaveDraw 文件上传漏洞复现

0x01 产品简介 OfficeWeb365 是专注于 Office 文档在线预览及PDF文档在线预览云服务,包括 Microsoft Word 文档在线预览、Excel 表格在线预览、Powerpoint 演示文档在线预览,WPS 文字处理、WPS 表格、WPS 演示及 Adobe PDF 文档在线预览。 0x02 漏洞概述 OfficeWeb365 Sav…

Win10 安装.NET Framework 3.5 报错0x80240438

环境&#xff1a; Win10专业版 NET Framework 3.5 问题描述&#xff1a; Win10 安装.NET Framework 3.5 报错0x80240438 解决方案&#xff1a; 1.检查自动更新服务是否未开启&#xff0c;开启自动更新失败&#xff0c;用工具开启自动更新,重启电脑&#xff08;未解决&am…

Proxmox创建Windows虚拟机

文章目录 下载ISO安装文件上传 下载ISO安装文件 下载地址&#xff1a;https://www.xitongzhijia.net/ 也可去官网进行下载 上传 将下载的ISO文件上传到Proxmox 选择ISO文件进行上传 上传后再ISO镜像中可以看到安装文件 点击创建虚拟机 填写名称&#xff0c;不能填写中文 镜…

团建策划信息展示服务预约小程序效果如何

团建是中大型企业商家每年举办的员工活动&#xff0c;其形式多样化、具备全部参与的娱乐性。但在实际策划流程及内容时&#xff0c;部分公司便会难以入手&#xff0c;术业有专攻&#xff0c;这个时候团建策划公司便会发挥效果。 如拓展训练、露营、运动会、体育竞技等往往更具…

[Linux] 基于LAMP架构安装论坛

一、安装Discuz论坛 1.1 创建数据库&#xff0c;并进行授权 mysql -u root -p123CREATE DATABASE bbs; #创建一个数据库GRANT all ON bbs.* TO bbsuser% IDENTIFIED BY admin123; #把bbs数据库里面所有表的权限授予给bbsuser,并设置密码admin123flush privileges; #刷新数据库…

Linux系统调试课:I2C tools调试工具

文章目录 一、如何使用I2C tools测试I2C外设1、I2C tools概述: 2、下载I2C tools源码:3、编译I2C tools源码: 4、i2cdetect 5、i2cget 6、i2cdump

docker基本管理和相关概念

docker是什么&#xff1f; docker是开源的应用容器引擎。基于go语言开发的。运行在Linux系统当中开源轻量级的“虚拟机”。 docker的容器技术可以在一台主机上轻松的为任何应用创建一个轻量级的&#xff0c;可移植的&#xff0c;自给自足的容器。 docker的宿主机是Linux系统…

天池SQL训练营(四)-集合运算-表的加减法和join等

-天池龙珠计划SQL训练营 4.1表的加减法 4.1.1 什么是集合运算 集合在数学领域表示“各种各样的事物的总和”, 在数据库领域表示记录的集合. 具体来说,表、视图和查询的执行结果都是记录的集合, 其中的元素为表或者查询结果中的每一行。 在标准 SQL 中, 分别对检索结果使用 U…

Spring Cloud gateway - CircuitBreaker GatewayFilte

前面学习Spring cloud gateway的时候&#xff0c;做测试的过程中我们发现&#xff0c;Spring Cloud Gateway不需要做多少配置就可以使用Spring Cloud LoadBalance的功能&#xff0c;比如&#xff1a; spring:application:name: spring-gatewaycloud:gateway:routes:- id: path…

Python 自动化之修理PDF文件(二)

PDF文件_合并与拆分PDF文档Pro版本 文章目录 PDF文件_合并与拆分PDF文档Pro版本前言一、要做成什么样子二、主要用到的函数三、基本思路1.引入库2.创建用户输入模块3.确定主框架 四、文档合并代码模块1.用户输入和函数调用2.引导用户输入文档信息3.合并文档内容4.命名新文档生成…

玩转大数据13: 数据伦理与合规性探讨

1. 引言 随着科技的飞速发展&#xff0c;数据已经成为了现代社会的宝贵资产。然而&#xff0c;数据的收集、处理和利用也带来了一系列的伦理和合规性问题。数据伦理和合规性不仅关乎个人隐私和权益的保护&#xff0c;还涉及到企业的商业利益和社会责任。因此&#xff0c;数据…