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

synchronized关键字相关

synchronized是Java中用于解决并发问题的核心关键字,它通过确保多个线程对共享资源的互斥访问,来避免线程安全问题(如竞态条件、数据不一致等)。
synchronized的核心特性
  • 原子性(Atomicity):确保一个或多个操作要么全部执行成功,要么全部执行失败。在synchronized代码块中的代码是不可中断的,同一时刻只有一个线程能执行。
  • 可见性(Visibility):保证一个线程对共享变量的修改,对于其他后续进入同步代码块的线程是可见的。当线程释放锁时,会将私有内存中的变量值刷新回主内存。
  • 有序性(Ordering):虽然编译器和处理器为了优化会进行指令重排,但synchronized可以保证多线程程序在逻辑上的执行顺序,即“同步块内的代码在执行上具有先后顺序”。
  • 可重入性:可重入性是指一个线程已经获取到某个锁后,再次请求该锁时可以直接获取,无需重新竞争。synchronized是可重入锁,其内部通过计数器记录锁的持有次数(初始为0,获取锁时加1,释放锁时减1,计数器为0时锁才被真正释放)。这一特性避免了线程在递归调用同步方法/代码块时出现死锁。例如,一个同步方法A调用另一个同步方法B(两者锁对象相同),线程获取A的锁后,调用B时可直接获取锁。
synchronized的使用方式
synchronized的使用灵活,可修饰不同的代码结构,核心是明确“锁对象”——线程竞争的是锁对象,只有获取到锁对象的线程才能执行同步代码,常见使用方式有3种。
  1. 修饰实例方法(对象锁),语法:public synchronized void methodName() { ... }
  • 锁对象:当前类的实例对象(this)。
  • 特点:不同实例对象的锁相互独立,即多个线程访问同一个实例的同步实例方法时会竞争锁;访问不同实例的同步实例方法时,因锁对象不同,不会竞争。
  • 示例:同一User实例的add()方法被多线程调用时互斥,不同User实例的add()方法可并行执行。
  1. 修饰静态方法(类锁)语法:public static synchronized void methodName() { ... }
  • 锁对象:当前类的Class对象(每个类在JVM中只有一个Class对象,是全局唯一的)。
  • 特点:类锁是全局锁,无论创建多少个类的实例,所有线程访问该类的同步静态方法时,都会竞争同一个Class对象锁。
  • 注意:类锁与对象锁相互独立,即同步静态方法和同步实例方法的锁对象不同,线程访问时不会竞争。
  1. 修饰代码块(自定义锁对象),语法:synchronized (锁对象) { ... 同步代码 ... }
  • 锁对象:可自定义,支持两种类型:① 实例对象(this或其他实例);② Class对象(类名.class)。
  • 特点:粒度最细,可精准控制需要同步的代码片段(而非整个方法),减少锁竞争,提高程序性能。
  • 常见场景:
    • 锁当前实例:synchronized (this) { ... },效果与修饰实例方法一致,但仅同步代码块内的逻辑。
    • 锁Class对象:synchronized (User.class) { ... },效果与修饰静态方法一致。
    • 锁自定义对象:private Object lock = new Object(); synchronized (lock) { ... },通过独立的锁对象,避免与其他同步逻辑竞争锁,灵活性最高。
synchronized的锁机制
  • Java 6及以后对synchronized进行了大幅优化,引入了“偏向锁、轻量级锁、重量级锁”三种锁状态,目的是根据线程竞争的激烈程度动态切换锁状态,平衡性能与线程安全。锁机制的核心是“对象头”——Java对象在内存中的布局包括对象头、实例数据、对齐填充,其中对象头存储了锁的状态信息(Mark Word)、类元数据指针等。
  • 三种锁状态的优先级:无锁 < 偏向锁 < 轻量级锁 < 重量级锁,随着线程竞争的加剧,锁会从低级别向高级别升级,且升级过程不可逆(一旦升级为重量级锁,无法回退为轻量级锁或偏向锁)。
    • 无锁(No Lock):初始状态。
    • 偏向锁(Biased Lock):当只有一个线程访问同步块时,直接在对象头记录线程ID,下次该线程进入时无需CAS操作,性能极高。
    • 轻量级锁(Lightweight Lock):当出现竞争,但竞争不激烈时,通过CAS自旋来尝试获取锁,避免线程阻塞。
    • 重量级锁(Heavyweight Lock):当自旋超过一定次数或竞争非常激烈时,升级为重量级锁。此时未获取到锁的线程会进入阻塞(Blocked)状态,交给操作系统管理。
锁优化补充
  • 锁消除:JVM的即时编译器(JIT)在运行时,会对一些“不可能存在竞争的锁”进行消除。例如,局部变量作为锁对象(每个线程都有独立的局部变量,无共享),JVM会直接删除该synchronized修饰,避免不必要的锁开销。
  • 锁粗化:当多个连续的synchronized代码块使用同一个锁对象时,JVM会将这些代码块合并为一个大的同步代码块,减少锁的获取/释放次数(每次获取/释放锁都有开销)。例如,循环内多次调用同步方法,JVM可能将锁粗化到循环外部。

CAS (Compare And Swap,比较并交换) 是并发编程中实现原子操作的核心算法,是一种乐观锁的实现策略。
  1. CAS的工作原理,包含三个核心参数:
  • 内存地址 V (Memory Location):变量在内存中的实际值。
  • 期望值 A (Expected Value):线程认为该变量当前应该是什么值。
  • 新值 B (New Value):线程想要更新成的值。
  • 执行逻辑:当且仅当内存地址 V 的值等于期望值 A 时,处理器才会将 V 的值更新为 B。否则,说明该变量已被其他线程修改,当前线程什么都不做,通常会进入自旋(死循环重试)。
  1. CAS的优缺点
    优点:
  • 非阻塞性:CAS 是一种非阻塞算法(Non-blocking),它不需要像 synchronized 那样挂起和恢复线程。
  • 性能高:在低、中度竞争的情况下,由于减少了线程上下文切换的开销,效率远高于重量级锁。
    缺点:
  • 循环时间长(自旋开销):如果高并发下竞争激烈,CAS 会频繁失败并不断自旋,这会给 CPU 带来巨大的计算压力。
  • 只能保证一个共享变量的原子操作:对于多个变量的操作,仍需使用 synchronized 或 ReentrantLock。
  • ABA 问题(最经典的缺点)。
  1. 什么是 ABA 问题?
  • 如果变量初始值为A,在线程1准备修改它的过程中,线程2快速地将其改成了B,然后又改回了A。 现象:线程1观察到值依然是A,认为它没变过,于是CAS成功。 风险:虽然数值没变,但变量的状态(或对象内部的属性)可能已经发生了变化,导致逻辑错误。
  • Java提供了AtomicStampedReference类,通过引入版本号(Stamp)来解决: 每次变量更新时,不仅更新值,还增加一个版本号。只有值和版本号都一致,CAS才会成功。
  • 还可以设置时间戳来解决。
  1. CAS 在Java中的实现
  • 在Java中,CAS主要由 sun.misc.Unsafe 类提供支持。该类中的方法(如 compareAndSwapInt)是 native 的,直接调用硬件底层的指令。

ReentrantLock(可重入锁)
  • ReentrantLock是 Java java.util.concurrent.locks 包下的可重入锁实现,基于 AQS(抽象队列同步器)构建,是 synchronized 的 “增强版”—— 既保留了 synchronized 的可重入特性,又提供了更灵活的同步控制能力。
两者对比:
特性synchronizedReentrantLock
实现层面JVM 层面(关键字),由 C++ 实现JDK 层面(API),由 Java 编写(基于 AQS)
锁的释放自动释放(代码执行完或异常后)手动释放(必须在 finally 中调用 unlock())
灵活性低(不可中断,无超时机制)高(支持尝试获取、超时获取、可中断获取)
公平性只支持非公平锁支持公平锁与非公平锁(默认非公平)
等待队列只能关联 1 个 等待队列(wait/notify)可以绑定 多个 Condition(精细化唤醒)
ReentrantLock的特有高级功能
  1. 响应中断 (lockInterruptibly)
  • synchronized一旦进入阻塞等待,除非拿到锁,否则无法被中断。而ReentrantLock允许线程在等待锁的过程中响应Thread.interrupt(),从而避免死等。
  1. 超时机制 (tryLock)
  • 线程可以尝试获取锁,如果锁被占用,立即返回 false 或者等待一段时间后返回,而不是一直阻塞。这在预防死锁时非常有用。
  1. 公平锁 (Fairness)
  • 公平锁:按照线程请求锁的顺序分配,先到先得。
  • 非公平锁(默认):允许“插队”。如果新来的线程正好碰到锁释放,它可以直接抢占,性能通常比公平锁高。
  1. 多个 Condition 对象
  • 通过lock.newCondition(),你可以创建多个等待集。例如在阻塞队列中,可以定义 notFull 和 notEmpty 两个条件,实现比 notifyAll 更精准的线程唤醒。
基本使用示例:
import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockDemo { // 创建非公平锁(默认),若需公平锁:new ReentrantLock(true) private static final ReentrantLock lock = new ReentrantLock(); public static void doTask() { // 1. 普通获取锁(不可中断) lock.lock(); try { // 临界区代码(线程安全) System.out.println(Thread.currentThread().getName() + " 执行任务"); Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // 必须在finally中释放锁,否则锁永远无法释放 lock.unlock(); } } // 超时获取锁示例 public static void tryLockWithTimeout() { try { // 尝试在2秒内获取锁,获取成功返回true,失败返回false if (lock.tryLock(2, java.util.concurrent.TimeUnit.SECONDS)) { try { System.out.println(Thread.currentThread().getName() + " 超时获取锁成功"); } finally { lock.unlock(); } } else { System.out.println(Thread.currentThread().getName() + " 超时获取锁失败"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } public static void main(String[] args) { // 测试普通获取锁 new Thread(ReentrantLockDemo::doTask, "线程1").start(); new Thread(ReentrantLockDemo::doTask, "线程2").start(); // 测试超时获取锁 new Thread(ReentrantLockDemo::tryLockWithTimeout, "线程3").start(); } }
http://www.jsqmd.com/news/572812/

相关文章:

  • 告别阻塞!Qt多进程通信的5种高效事件循环方案对比
  • Vanilla论坛邮件通知系统配置:确保用户及时获取社区动态
  • 前端PWA:让你的网站变成App
  • FindPatterns与PatMax算法对比:康耐视InSight电子表格模式下如何选择图案匹配工具?
  • 基于KNN算法 Python的隶书字体识别系统设计与实现
  • embeddinggemma-300m部署详解:Ollama中嵌入服务健康检查与日志分析
  • 2026年终极指南:如何轻松重置JetBrains IDE试用期,告别30天限制困扰
  • Temu跨境电商2026年创业指南:在家运营实操与避坑
  • 前端GraphQL客户端:优雅地获取数据
  • Anything XL开源镜像实战:safetensors单文件加载原理与校验方法详解
  • 自动药片装瓶机 No.360 三菱 组态王 基于PLC的药片装瓶自动控制系统 我们主要的后发送...
  • 给娃的编程启蒙:用Air001和Arduino做个会闪灯、会说话的电子宠物(附完整代码)
  • YOLO-v8.3新手避坑指南:显存优化技巧与最佳实践
  • 【郑州大学主办,多学院学会承协办| ACM ICPS 出版(有ISBN号) |往届已被EI Compendex、Scopus检索】第二届生物信息学与计算生物学国际学术会议(ISBCB 2026)
  • 《Camera Graph:跨摄像机追踪的核心秘密》——视频系统如何从“单点感知”进化到“全域认知”
  • 一文读懂 Vref:原理与使用要点-CSDN博客
  • 资源捕获浏览器扩展:3步掌握高效媒体提取工具
  • 多语种视频本地化利器:Heygem数字人系统,同一内容多种语言输出
  • Profinet转Devicenet网关应用中易忽略的接线问题
  • 忍者像素绘卷图文教程:硬边阴影UI+RPG交互逻辑实操详解
  • 德意志飞机通过全球协作升级支线航空驾驶舱人机工学
  • 别再被Windows自动维护坑电量!保姆级禁用唤醒定时器教程(附电源计划优化)
  • AnotherRedisDesktopManager:Redis可视化管理终极指南,5分钟快速上手
  • 如何高效解决Visual C++ Redistributable组件问题并建立长效管理机制
  • Phi-4-mini-reasoning在ollama中如何做不确定性推理?概率建模与贝叶斯推断示例
  • 数字图像处理——图像处理算子体系梳理
  • AI+Python 双驱动计量经济学:从多源数据处理到 SCI 论文--多源数据处理、机器学习预测及复杂因果识别全流程实战随机森林模型核心技术
  • 从零实现3DGS的simple-knn:用PyTorch C++/CUDA扩展复现点云局部特征提取
  • UV更改python源和pypi源
  • 链表操作精讲:删除与反转实战