当前位置: 首页 > news >正文

Synchronized使用

文章目录

    • synchronized使用
    • 基本概念
    • 使用方法
    • 实现原理
    • 锁的粒度
    • 并发编程注意事项
    • 与Lock锁对比比较
    • 线程安全性与性能

synchronized使用

当涉及到多线程编程时,保证数据的正确性和一致性是至关重要的。而synchronized关键字是Java语言中最基本的同步机制之一,它可以有效地确保在多线程环境下共享资源的安全访问。synchronized关键字可以应用于方法、代码块或静态方法上,用于实现对共享资源的同步访问。

在这里插入图片描述

我们将讨论synchronized的适用场景和一些最佳实践。通常情况下,synchronized适用于对共享资源的访问控制,例如对共享变量、实例方法或静态方法的访问控制。但需要注意的是,过多地使用synchronized可能会导致性能问题,因此应该尽量减少同步块的范围,避免长时间持有锁。

基本概念

synchronized是Java语言中用于实现线程同步的关键字,它提供了一种简单而有效的机制来确保多个线程对共享资源的安全访问。让我们从基本概念的角度来深入理解synchronized的使用。

在这里插入图片描述

  1. 线程安全性:在多线程编程中,当多个线程同时访问共享资源时,可能会出现数据竞争和不一致性的问题。synchronized关键字可以帮助解决这些问题,通过在关键代码块或方法前加上synchronized关键字,可以确保同一时刻只有一个线程可以执行这段代码,从而避免了竞态条件(Race Condition)的发生。

  2. 对象锁:synchronized关键字是基于对象的监视器锁(Monitor Lock)实现的。每个Java对象都可以作为一个锁,当一个线程进入synchronized代码块或方法时,它会尝试获取对象的锁。如果锁已经被其他线程持有,则当前线程会被阻塞,直到获取到锁为止。这样就保证了同一时刻只有一个线程可以执行被synchronized保护的代码。

  3. 锁的释放:当一个线程执行完synchronized代码块或方法后,会释放对象的锁,其他线程可以竞争获取锁并执行相应的代码。这种锁的释放机制保证了线程的公平性和资源的合理利用,避免了某个线程长时间占用锁而导致其他线程无法访问共享资源的情况。

  4. 锁的粒度:synchronized关键字可以应用于不同的粒度,包括对象方法、静态方法和代码块。在选择锁的粒度时,需要根据具体的业务需求和性能考虑来决定。通常情况下,应该尽量减小锁的粒度,避免长时间持有锁导致性能下降。

使用方法

synchronized关键字可以用于不同的场景和粒度,包括对象方法、静态方法和代码块。让我们从使用方法的角度来深入了解synchronized的使用,并通过具体示例来说明。

  1. 对象方法的同步:可以使用synchronized关键字修饰对象方法,确保同一时刻只有一个线程可以访问该对象的同步方法。示例如下:
public class SynchronizedExample {private int count = 0;// 同步方法public synchronized void increment() {count++;}// 非同步方法public void decrement() {count--;}public int getCount() {return count;}
}

increment()方法被修饰为synchronized,因此在执行increment()方法时会获取对象的锁,其他线程必须等待锁释放后才能执行。而decrement()方法没有被synchronized修饰,因此不受锁的影响,可能会导致线程不安全。

  1. 静态方法的同步:可以使用synchronized关键字修饰静态方法,确保同一时刻只有一个线程可以访问该类的同步静态方法。示例如下:
public class SynchronizedExample {private static int count = 0;// 静态同步方法public static synchronized void increment() {count++;}// 非静态方法public void decrement() {count--;}public static int getCount() {return count;}
}

increment()方法被修饰为静态synchronized,因此在执行increment()方法时会获取类的锁,其他线程必须等待锁释放后才能执行。decrement()方法没有被synchronized修饰,因此不受锁的影响。

  1. 代码块的同步:可以使用synchronized关键字修饰代码块,只对代码块内部的代码进行同步控制,粒度更细。示例如下:
public class SynchronizedExample {private Object lock = new Object();private int count = 0;public void increment() {synchronized (lock) {count++;}}public int getCount() {synchronized (lock) {return count;}}
}

通过synchronized关键字修饰代码块,并传入一个锁对象lock,确保在执行代码块内部的代码时获取lock对象的锁,从而实现同步。

实现原理

理解synchronized关键字的实现原理有助于我们更深入地理解其在Java多线程编程中的作用和效果。基于对象头中的锁标志位实现的,当一个线程访问synchronized代码块时,会尝试获取对象的锁,如果锁已经被其他线程获取,则当前线程会被阻塞,直到获取到锁为止。

在这里插入图片描述

  1. 对象锁和监视器锁:在Java中,每个对象都与一个监视器锁(Monitor Lock)相关联,也称为对象锁。当一个线程进入synchronized代码块或方法时,它会尝试获取对象的监视器锁。如果锁已被其他线程持有,则该线程会被阻塞,直到获取到锁为止。

  2. 对象头中的标志位:Java对象的存储结构中包含了对象头信息,其中包括用于存储锁状态的标志位。当一个对象被synchronized修饰时,Java虚拟机会自动使用这些标志位来管理对象的锁状态。

  3. 互斥性和排他性:synchronized关键字确保了对于同步代码块或方法的访问是互斥的,即同一时刻只有一个线程可以持有对象的锁,并且其他线程必须等待锁释放后才能执行同步代码块或方法。

  4. 锁的释放:当一个线程执行完synchronized代码块或方法后,会释放对象的锁,这样其他线程就可以竞争获取锁并执行相应的代码。这种锁的释放机制保证了线程的公平性和资源的合理利用,避免了某个线程长时间占用锁而导致其他线程无法访问共享资源的情况。

  5. 内存可见性:除了提供互斥性和排他性外,synchronized关键字还提供了内存可见性。即当一个线程释放锁时,它所做的修改对其他线程都是可见的。这确保了在多线程环境下的内存一致性。

锁的粒度

锁的粒度过细可能导致线程竞争过高,性能下降,而锁的粒度过粗则可能会造成资源的浪费,应根据实际情况选择适当的锁粒度。从锁的粒度角度来理解synchronized的使用是非常重要的,因为锁的粒度直接影响到多线程程序的性能和并发度。让我们深入探讨不同粒度的锁,并讨论如何选择合适的锁粒度来保证线程安全并提高性能。

在这里插入图片描述

1 对象级别的锁

当synchronized修饰实例方法时,它使用的是对象级别的锁。这意味着每个实例对象都有自己的锁,因此同一时刻只有一个线程可以访问该对象的synchronized方法。这种锁的粒度较细,可以保证线程安全,但可能会导致性能瓶颈,特别是在高并发场景下,因为不同对象之间的锁互不干扰,无法并行执行。

public class MyClass {private int count = 0;public synchronized void increment() {count++;}
}

2 类级别的锁

当synchronized修饰静态方法时,它使用的是类级别的锁。这意味着同一时刻只有一个线程可以访问该类的synchronized静态方法,无论是哪个实例对象。这种锁的粒度较粗,因为它涉及整个类的所有实例,可能会导致一些不必要的阻塞和性能下降。

public class MyClass {private static int count = 0;public static synchronized void increment() {count++;}
}

3 代码块级别的锁

通过synchronized关键字修饰代码块,可以控制锁的粒度,从而在一定程度上平衡了线程安全性和性能。通过控制代码块的范围,可以灵活地选择需要同步的代码片段,减小锁的粒度,提高并发度。

public class MyClass {private Object lock = new Object();private int count = 0;public void increment() {synchronized (lock) {count++;}}
}

在实际应用中,我们应该根据具体的业务场景和性能要求来选择合适的锁粒度。通常情况下,应该尽量减小锁的粒度,避免长时间持有锁而导致性能下降,从而实现更好的并发控制和性能优化。

并发编程注意事项

使用synchronized进行并发编程时,需要注意一些重要的事项,以确保线程安全性和程序正确性。

在这里插入图片描述

  1. 锁的粒度控制

锁的粒度应该尽量小,即尽量只对必要的代码块进行同步。过大的锁粒度可能会导致性能下降,因为多个线程会因为等待同一个锁而被阻塞。避免在整个方法内部使用synchronized修饰符,而是应该只对需要同步的代码块使用。

  1. 避免死锁

当多个线程相互等待对方释放锁时,就会发生死锁。为了避免死锁,应该避免在持有一个锁的同时去尝试获取另一个锁。如果必须要获取多个锁,可以尝试按照固定的顺序获取锁,以减少死锁的可能性。

  1. 释放锁的时机

确保在不需要锁的时候及时释放锁,避免长时间持有锁。这可以通过尽量减小同步代码块的范围来实现,以最大程度地提高并发度和性能。

  1. 避免嵌套锁

当一个线程在持有一个锁的同时尝试获取另一个锁时,就会出现嵌套锁。嵌套锁可能导致死锁,也会增加代码的复杂性和维护成本。尽量避免在同步代码块中嵌套使用synchronized关键字,如果必须要嵌套,务必谨慎处理。

  1. 注意多线程共享资源的安全访问

对于共享资源的访问,必须确保在任何时刻只有一个线程在修改资源,以避免竞态条件和数据不一致性的问题。使用synchronized关键字确保对共享资源的安全访问,或者考虑使用并发安全的数据结构来避免手动加锁。

  1. 性能优化和锁的选择

对于高并发的场景,应该进行性能优化,避免过多地使用synchronized来提高并发度。可以考虑使用java.util.concurrent包中提供的更高级别的锁机制,如ReentrantLock,以及并发安全的数据结构来提高性能和并发度。

与Lock锁对比比较

synchronized和Lock锁是Java中两种常用的线程同步机制,它们在实现线程安全性和控制并发访问方面有着不同的特点和适用场景。synchronized适用于简单的同步场景,并且使用方便,但功能相对有限;而Lock锁提供了更多的功能和灵活性,适用于复杂的并发控制场景。在选择使用时,可以根据具体需求和场景来决定使用哪种锁机制。

在这里插入图片描述

  1. 同步粒度:

​ ○ synchronized锁的粒度较粗,它可以修饰方法或代码块,锁的范围是整个方法或代码块。

​ ○ Lock锁的粒度较细,它需要显式地获取和释放锁,可以在任意位置获取和释放锁,因此可以更灵活地控制锁的范围。

  1. 可重入性:

​ ○ synchronized是可重入锁,同一个线程可以重复获取同一个对象的锁,而不会产生死锁。

​ ○ Lock锁也是可重入的,通过ReentrantLock实现了可重入性,同样支持同一线程多次获取锁。

  1. 可中断性:

​ ○ synchronized在获取锁时是不可中断的,一旦获取不到锁,线程将一直阻塞直到获取到锁。

​ ○ Lock锁提供了可中断的获取锁方式,通过lockInterruptibly()方法可以在等待锁的过程中响应中断。

  1. 公平性:

​ ○ synchronized锁是非公平锁,无法保证等待时间最长的线程优先获得锁。

​ ○ Lock锁可以通过构造函数指定是否是公平锁,从而可以实现公平或非公平的锁。

  1. 灵活性:

​ ○ Lock锁提供了更多的功能和扩展性,比如支持条件变量、多个条件、超时获取锁等功能,更适合复杂的并发控制场景。

​ ○ synchronized相对简单,适用于简单的同步需求,代码更简洁。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockExample {private int count = 0;private Lock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {lock.lock();try {return count;} finally {lock.unlock();}}
}

使用ReentrantLock实现了对increment()和getCount()方法的线程安全控制,通过lock()和unlock()方法手动获取和释放锁。

线程安全性与性能

使用synchronized关键字确实能够简化线程安全性的实现,因为它提供了对代码块或方法的互斥访问。然而,从性能的角度来看,synchronized在某些情况下可能会带来一些额外的开销,特别是在高并发的情况下。

在这里插入图片描述

1 线程安全性:

synchronized关键字确保了同一时刻只有一个线程可以进入被同步的代码块或方法,从而保证了共享资源的安全访问。由于synchronized是在Java语言层面提供的同步机制,因此它的实现是可靠的,不会出现一些低级别的并发问题,如死锁、活锁等。

2 性能影响:

在低并发情况下,synchronized的性能影响往往可以忽略不计,因为线程之间的竞争较少,获取锁的开销相对较小。然而,在高并发情况下,synchronized可能会成为性能瓶颈。因为每个线程在进入同步代码块时都需要获取对象的锁,并且有可能会因为锁竞争而被阻塞,导致性能下降。synchronized的粒度较粗,可能会导致一些不必要的阻塞和等待,进而影响整个程序的并发度和性能。

针对synchronized可能带来的性能问题,可以考虑以下优化措施:

●减小锁的粒度:尽量将同步代码块的范围减小到最小,只同步必要的代码片段,从而减少线程之间的竞争和阻塞。

●优化共享资源的访问:考虑使用更高效的数据结构或算法来减少共享资源的访问次数,从而减少锁竞争的概率。

●使用更高级别的锁机制:如java.util.concurrent包中提供的ReentrantLock,它提供了更多的功能和灵活性,可以更精细地控制锁的获取和释放。

虽然synchronized关键字确保了线程安全性,但在高并发情况下可能会成为性能瓶颈。因此,在实际应用中,需要权衡考虑线程安全性和性能之间的平衡,选择合适的同步机制来确保程序的正确性和性能。

相关文章:

Synchronized使用

文章目录 synchronized使用基本概念使用方法实现原理锁的粒度并发编程注意事项与Lock锁对比比较线程安全性与性能 synchronized使用 当涉及到多线程编程时,保证数据的正确性和一致性是至关重要的。而synchronized关键字是Java语言中最基本的同步机制之一&#xff0…...

OpenStack四种创建虚拟机的方式

实例(Instances)是在云内部运行的虚拟机。您可以从以下来源启动实例: 一、上传到镜像服务的镜像(Image) 使用已上传到镜像服务的镜像来启动实例。 二、复制到持久化卷的镜像(Volume) 使用已…...

Expo运行模拟器失败错误解决(xcrun simctl )

根据你的描述,问题主要涉及两个方面:xcrun simctl 错误和 Expo 依赖版本不兼容。以下是针对这两个问题的解决方案: 解决 xcrun simctl 错误 错误代码 72 通常表明 simctl 工具未正确配置或路径未正确设置。以下是解决步骤: 确保 …...

Docker从入门到精通- 容器化技术全解析

第一章:Docker 入门 一、什么是 Docker? Docker 就像一个超级厉害的 “打包神器”。它能帮咱们把应用程序和它运行所需要的东东都整整齐齐地打包到一起,形成一个独立的小盒子,这个小盒子在 Docker 里叫容器。以前呢,…...

开启对话式智能分析新纪元——Wyn商业智能 BI 携手Deepseek 驱动数据分析变革

2月18号,Wyn 商业智能 V8.0Update1 版本将重磅推出对话式智能分析,集成Deepseek R1大模型,通过AI技术的深度融合,致力于打造"会思考的BI系统",让数据价值触手可及,助力企业实现从数据洞察到决策执…...

RabbitMQ 消息顺序性保证

方式一:Consumer设置exclusive 注意条件 作用于basic.consume不支持quorum queue 当同时有A、B两个消费者调用basic.consume方法消费,并将exclusive设置为true时,第二个消费者会抛出异常: com.rabbitmq.client.AlreadyClosedEx…...

防御保护作业二

拓扑图 需求 需求一: 需求二: 需求三: 需求四: 需求五: 需求六: 需求七: 需求分析 1.按照要求进行设备IP地址的配置 2.在FW上开启DHCP功能,并配置不同的全局地址池,为…...

Spring Boot中实现多租户架构

文章目录 Spring Boot中实现多租户架构多租户架构概述核心思想多租户的三种模式优势挑战租户识别机制1. 租户标识(Tenant Identifier)2. 常见的租户识别方式3. 实现租户识别的关键点4. 租户识别示例代码5. 租户识别机制的挑战数据库隔离的实现1. 数据库隔离的核心目标2. 数据…...

【AI-27】DPO和PPO的区别

DPO(Direct Preference Optimization)和 PPO(Proximal Policy Optimization)有以下区别: 核心原理 DPO:基于用户偏好或人类反馈直接优化,核心是对比学习或根据偏好数据调整策略,将…...

Git stash 暂存你的更改(隐藏存储)

一、Git Stash 概述 在开发的时候经常会遇到切换分支时需要你存储当前的更改,如果你暂时不想应用当前更改也不想放弃更改,那么你可以使用 git stash先将其隐藏存储,这样代码就会变成未修改的状态,等解决其他问题后,在…...

负载测试和压力测试的原理分别是什么

负载测试和压力测试是性能测试的两种主要类型,它们的原理和应用场景有所不同。 负载测试(Load Testing) 原理: 负载测试通过模拟实际用户行为,逐步增加系统负载,观察系统在不同负载下的表现。目的是评估系…...

shell脚本控制——定时运行作业

在使用脚本时,你也许希望脚本能在以后某个你无法亲临现场的时候运行。Linux系统提供了多个在预选时间运行脚本的方法:at命令、cron表以及anacron。每种方法都使用不同的技术来安排脚本的运行时间和频率。接下来将依次介绍这些方法。 1.使用at命令调度作…...

LeetCode 热题 100 回顾

目录 一、哈希部分 1.两数之和 (简单) 2.字母异位词分组 (中等) 3.最长连续序列 (中等) 二、双指针部分 4.移动零 (简单) 5.盛最多水的容器 (中等) 6…...

HTML5--网页前端编程(上)

HTML5–网页前端编程(上) 1.网页 (1)网站是根据一定的规则,使用html制作的相关的网页的集合。 网页是网站上的一页,通常是html格式的文件,他要通过浏览器来阅读。网页是网站的基本元素,由图片链接声音文字等元素造成,以.html或.htm后缀结尾的文件称为html文件。 (2…...

气体控制器联动风机,检测到环境出现异常时自动打开风机进行排风;

一、功能:检测到环境出现异常时自动打开风机进行排风; 二、设备: 1.气体控制器主机:温湿度,TVOC等探头的主机,可上报数据,探头监测到异常时,主机会监测到异常可联动风机或声光报警…...

示波器使用指南

耦合方式 在示波器中,耦合方式决定了信号源与示波器输入之间的信号传输方式。具体来说,直流耦合、交流耦合和接地耦合这三种方式有不同的工作原理和应用场景,下面是它们的差异: 1. 直流耦合(DC Coupling)…...

Post-trained猜想

强化 -- 输出Action 真实的避障 ActionCond 输入Action 生成视频 原来只是仿真 没有和整个的机器人系统结合 gym生成视频 不需要后处理 obersation...

javaEE-10.CSS入门

目录 一.什么是CSS ​编辑二.语法规则: 三.使用方式 1.行内样式: 2.内部样式: 3.外部样式: 空格规范 : 四.CSS选择器类型 1.标签选择器 2.类选择器 3.ID选择器 4.通配符选择器 5.复合选择器 五.常用的CSS样式 1.color:设置字体颜色 2.font-size:设置字体大小 3…...

eclipse配置Spring

1、从eclipse下载Spring工具 进入 help – install new software… ,如下图: 点击 add ,按以下方式输入: Name : Spring Location : http://dist.springsource.com/release/TOOLS/update/e4.10/ 之后点击 add ,等待…...

爬虫技巧汇总

一、UA大列表 USER_AGENT_LIST 是一个包含多个用户代理字符串的列表,用于模拟不同浏览器和设备的请求。以下是一些常见的用户代理字符串: USER_AGENT_LIST [Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; Hot Lingo 2.0),Mozilla…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

基于 TAPD 进行项目管理

起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...