news 2026/6/10 7:18:55

开关用 volatile,排队用 synchronized,复杂用 Lock:一文理清并发三兄弟

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
开关用 volatile,排队用 synchronized,复杂用 Lock:一文理清并发三兄弟

在 Java 并发编程中,volatilesynchronizedLock是最常用的三种同步机制。很多人能说出它们的区别,却说不清各自最适合用在什么地方

这篇文章不用代码,只从“解决什么问题”的角度,帮你彻底理清它们的关系与运用场景。

一、先一句话概括各自角色

关键字/接口一句话概括本质
volatile保证变量修改的可见性和有序性,但不保证原子性JVM 轻量级同步机制
synchronized保证代码块的原子性、可见性、有序性,基于 monitor 机制内置锁(重量级,可优化)
Locksynchronized的 API 版本,提供更灵活的控制(可中断、可超时、公平锁等)显式锁(JUC 提供)

二、它们的关系是递进的

  • volatile是基础
    它只解决了“一个线程写,多个线程读”的可见性问题。无法解决多个线程同时写(如count++)的原子性问题。

  • synchronized是升级
    用锁把代码块包起来,同一时刻只允许一个线程执行。但它较重,获取锁的线程必须阻塞等待,且不能中断、不能超时。

  • Lock是增强
    提供了synchronized没有的能力:可中断、可超时、公平锁、多个等待队列(Condition)。

功能强弱/灵活度:
volatile<synchronized<Lock

使用复杂度:
volatile<synchronized<Lock

性能(现代 JVM):
volatilesynchronized(优化后) ≈Lock(CAS 实现)

三、一句话概括它们各自负责什么

  • volatile:我改了你必须立刻看到 ——可见性

  • synchronized:一次只让一个人进来,别人在外面等着 ——互斥 + 可见性

  • Lock:像synchronized一样互斥,但我可以随时不排队、中途走人、叫号公平点 ——更灵活的互斥

四、volatile 用在哪儿?

核心场景:一个线程写,多个线程读的状态标志

volatile不保证原子性,不能用于count++。它适合表达“事情发生了 / 状态变了”,其他线程需要立刻感知。

典型运用:

  • 开关控制(状态标志)
    后台轮询线程需要一个running变量控制它停止。服务关闭时,主线程设置running = false,工作线程看到后立即退出循环,实现优雅停机。

  • 双重检查锁(DCL)实现单例
    高并发下只创建一个对象实例。volatile阻止指令重排序,防止其他线程拿到未初始化完成的对象。

  • 读多写少的共享变量
    配置项、系统参数,偶尔修改但大量读取。例如config.refreshInterval修改后,所有工作线程立刻用上新值。

五、synchronized 用在哪儿?

核心场景:大家都可能改,必须排队的原子性操作

volatile解决不了“同时写”的问题,需要synchronized把代码块变成原子操作。

典型运用:

  • 计数器、累加器
    统计接口调用次数、在线人数、库存扣减。increment()方法必须排队执行,否则多线程同时 +1 会丢失计数。

  • 懒汉式单例(整个方法或代码块加锁)
    懒加载且保证只创建一个实例。直接锁住getInstance()方法,虽然效率低,但实现最简单。

  • 复合操作
    先检查后执行(如if (map.containsKey(key))map.get(key))。转账业务需要先判断余额再扣款,这两步必须一起锁住。

  • 操作非线程安全的集合
    多线程并发操作HashMapArrayList。维护一个缓存 Map 时,增删改查都锁住对象,防止读的时候有人删导致ConcurrentModificationException

  • JVM 层面的唯一操作(wait/notify)
    生产者-消费者模型中,wait()/notify()必须在synchronized块里调用。

六、Lock 用在哪儿?

核心场景:synchronized 功能不够用的地方

Lock提供了synchronized无法实现的精细控制:尝试加锁、超时加锁、可中断加锁。

典型运用:

  • 尝试获取锁
    拿不到锁就不想等,直接做别的事或返回错误。秒杀系统中,尝试获取锁,拿不到就直接告诉用户“太挤了,稍后再试”,而不是让用户卡死。

  • 可中断的锁
    锁等待时间可能很长,用户想主动取消操作。例如用户在 GUI 界面点击“取消搜索”,需要打断正在等待数据库锁的线程。

  • 带超时的锁
    避免某个线程异常导致其他线程无限等待。分布式任务调度中,抢锁的线程必须在 10 秒内完成任务并释放锁,否则锁自动失效。

  • 公平锁
    严格按先来后到获取锁,防止饥饿。例如银行叫号系统(理想情况下)不允许插队。

  • 多个等待队列(Condition)
    一个锁配合多个等待条件。有界阻塞队列中:队列空时,取元素线程等待“非空”信号;队列满时,存元素线程等待“未满”信号。一个锁配两个Condition,代码更清晰。

七、总结对比(运用场景)

机制核心战场典型应用一句话场景
volatile状态标志开关控制、DCL 单例一个线程改,其他线程立刻看
synchronized原子操作计数器、复合操作、wait/notify多个线程改,必须排队
Lock高级控制尝试锁、超时锁、可中断锁synchronized不够灵活时

最后一句口诀帮你记住

开关用 volatile,简单排队用 synchronized,复杂控制(超时、中断、公平)用 Lock。

如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、转发,让更多人理清 Java 并发中的这三个关键角色。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 7:18:51

助睿实验作业6-1:浏览器用户画像分析-大屏静态布局制作

目录 一、实验目的 二、实验环境 三、实验数据 四、整体分析框架 4.1 业务问题 4.2 大屏设计方案 4.3 项目整体说明 五、实验步骤 5.1 前期准备&#xff1a;图层管理&#xff08;切换大屏&#xff09; 5.2 添加【省份分布—基础平面地图】 ​编辑​编辑 5.3 添加【核…

作者头像 李华
网站建设 2026/6/10 7:18:06

AtomGit Flutter鸿蒙客户端:设置页面

纯展示型页面的设计 设置页面在整个应用中是一个独特的页面类型——它不需要自己的状态管理&#xff0c;不需要异步加载数据&#xff0c;不需要处理错误状态。它的全部数据来自已有的 Provider 和服务实例&#xff0c;进入页面时所有信息已经可用。 这种"纯消费"模式…

作者头像 李华
网站建设 2026/6/10 7:17:09

内网环境离线配置 Neovim + LazyVim 记录

最近想把自己外网环境中已经配置好的 Neovim LazyVim 迁移到内网环境。由于内网不能访问外网&#xff0c;所以不能直接在内网执行 git clone、:Lazy sync、:MasonInstall、:TSUpdate 这类命令&#xff0c;否则会因为无法下载插件和工具而失败。 本次环境大致如下&#xff1a;…

作者头像 李华
网站建设 2026/6/10 7:15:37

周报日报生成:职场办公工具的实用搭配思路

周报日报生成&#xff1a;职场办公工具的实用搭配思路很多职场人每周都要花费1到2小时整理周报日报&#xff0c;从聊天记录、会议纪要、项目文档里拼凑本周的工作内容&#xff0c;还要按照部门要求的格式调整措辞&#xff0c;稍不注意就会遗漏关键的项目进度或数据&#xff0c;…

作者头像 李华