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

Java Thread线程2—线程锁synchronized,Lock,volatile

synchronized

在 Java 中,synchronized 关键字一共有 4 种写法(按“锁的对象”划分),所有写法在 字节码层面本质相同:通过 对象头里的 monitor 来实现互斥

写法 1:同步实例方法(锁的是当前实例 this

public class Counter {private int count = 0;public synchronized void increment() {   // 等价于 synchronized(this) { ... }count++;}
}
  • 锁对象:当前实例 this

  • 适用场景:多个线程操作同一个对象实例时保证线程安全。

同步静态方法(锁的是 Class 对象)

public class Counter {private static int count = 0;public static synchronized void increment() { // 等价于 synchronized(Counter.class) { ... }count++;}
}
  • 锁对象Counter.class(类的元数据对象)

  • 适用场景:保护静态变量,防止多个线程同时修改类级别的共享数据。

同步代码块(锁的是任意指定对象)

public class Counter {private int count = 0;private final Object lock = new Object();public void increment() {synchronized (lock) {   // 锁的是自定义的 lock 对象count++;}}
}
  • 锁对象:任意显式指定的对象(lockthisCounter.class 等)

  • 优点:粒度更细,减少锁竞争范围,提高并发度。

同步代码块(锁的是 this

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

效果与“同步实例方法”完全相同,只是写法不同。

总结对照表(面试速记)

写法锁对象适用场景粒度
同步实例方法this保护实例变量
同步静态方法Class<?>保护静态变量
同步代码块(this)this与同步实例方法等价
同步代码块(任意对象)任意对象细粒度锁、减少竞争

锁与线程关系

只要多个方法共用同一把锁(比如默认的 this 或同一个 Class<?>),无论线程去访问哪一个带这把锁的方法,都必须串行地获取这把锁

举个例子

public class Demo {public synchronized void a() { /* ... */ }public synchronized void b() { /* ... */ }public synchronized void c() { /* ... */ }
}
  • 三个方法都隐式锁 this

  • 线程 T1 执行 a(),线程 T2 想执行 b(),线程 T3 想执行 c()

  • 结果:T2、T3 都会被挡在门外,直到 T1 释放锁

锁只认“对象”,不认“方法”;谁拿着同一把锁,谁就是临界区的唯一通行证。

Lock之ReentrantLock

基本范式(try-finally 必解锁)

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Counter {private final Lock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock();          // 获取锁(阻塞直到成功)try {count++;} finally {lock.unlock();    // 必须放在 finally,防止异常死锁}}
}

最常用、最安全的写法;无论正常还是异常路径,都能保证解锁。

可中断锁(lockInterruptibly()

public void incrementWithInterrupt() throws InterruptedException {lock.lockInterruptibly();  // 可被 Thread.interrupt() 打断try {count++;} finally {lock.unlock();}
}

尝试非阻塞锁(tryLock()

public boolean tryIncrement() {if (lock.tryLock()) {      // 立即返回,不阻塞try {count++;return true;} finally {lock.unlock();}} else {System.out.println("没抢到锁,直接干别的");return false;}
}

常用于 避免死锁带超时的快速失败 逻辑。

超时尝试锁(tryLock(long, TimeUnit)

public boolean incrementWithTimeout(long time, TimeUnit unit) throws InterruptedException {if (lock.tryLock(time, unit)) {   // 最多等待指定时间try {count++;return true;} finally {lock.unlock();}} else {System.out.println("等待超时,没抢到锁");return false;}
}

对比 synchronized 与 Lock

特性synchronizedLock (ReentrantLock)
语法关键字API 调用
可中断✅ (lockInterruptibly)
非阻塞尝试✅ (tryLock)
超时等待✅ (tryLock(timeout))
公平策略✅ (new ReentrantLock(true))
可绑定多个条件队列✅ (newCondition)

volatile

volatile 是 Java 提供的最轻量级同步机制,只保证“可见性”和“有序性”不保证“原子性”。在 Android 开发里,正确使用可以省去不少 synchronizedLock 的开销;用错则会埋坑

volatile 解决的是“一个线程改了,另一个线程立刻能看到”的问题,不能解决“多线程同时改”的问题。

作用点是否保证说明
可见性写后立即刷新到主存,读时从主存取
有序性(禁止重排)插入内存屏障,防止指令重排
原子性复合操作(i++、check-then-act)仍不安全

Android 典型使用场景

状态标志位

线程 A 改标志,线程 B 立即读,只有一个线程写

private volatile boolean isRunning = true;public void stop() {isRunning = false;   // 线程 A
}public void loop() {while (isRunning) {  // 线程 B...}
}

双重检查单例

class Singleton {private static volatile Singleton INSTANCE;public static Singleton get() {if (INSTANCE == null) {           // 1st checksynchronized (Singleton.class) {if (INSTANCE == null) {   // 2nd checkINSTANCE = new Singleton();}}}return INSTANCE;}
}

UI 可见性通信

Activity / Fragment 中后台线程更新单个变量状态:

private volatile int mProgress;  // 下载进度

synchronized 的对比

维度volatilesynchronized
作用范围单个变量代码块/方法
原子性
阻塞不会
性能极高较低(有优化

volatile 能保证线程之间立即可见禁止指令重排,适合一写多读纯标志位或双重检查单例;一旦涉及复合操作,仍需 synchronizedLockAtomic* 来保证原子性。”

禁止重排

没有 volatile,DCL 可能因为“先赋值、后初始化”的重排而暴露半初始化对象;volatile 的内存屏障把这两步顺序钉死,彻底杜绝该隐患。

重排前后对比

正常顺序(程序员视角)重排后(编译器/CPU 视角)
1. memory = allocate()1. memory = allocate()
2. ctor(memory)3. instance = memory ← 引用先发布
3. instance = memory2. ctor(memory) ← 初始化延后

带来的并发 bug

// 线程 A 执行
instance = new Singleton();   // 内部被重排成 1→3→2// 线程 B 几乎同时
if (instance != null) {       // 看到非 nullinstance.method();        // 实际对象还没初始化 → 崩溃或数据错误
}

加上 volatile

  • 读写都插入 内存屏障,禁止上述重排。

  • 线程 B 一定 看到“构造完成”的对象。

android中锁的使用

android 中使用lock锁多还是synchronized?

Android 日常业务代码 中,synchronized 远比 Lock 用得多;只有在需要 可中断、超时、公平锁、读写分离 等高级特性时,才会显式使用 ReentrantLock / ReentrantReadWriteLock。

统计数据与社区习惯

  • Google 官方示例、AOSP、Jetpack 源码里,95% 以上的同步场景都是 synchronizedvolatile

  • Kotlin 标准库提供的 @Synchronizedsynchronized(lock){} 进一步降低了使用门槛

  • 高并发框架(如 OkHttp、Glide)内部可能用到 ReentrantLock,但普通业务层很少需要。

特点对锁选择的影响
UI 主线程不容阻塞一般把耗时任务丢到线程池/HandlerThread,锁竞争通常较轻;synchronized 足够。
电量 & 内存敏感synchronized 在 ART 下经过 偏向锁、轻量级锁 优化,低开销;ReentrantLock 需要额外对象和队列,内存占用略高。
代码简洁性Android 开发节奏快,“写起来简单、维护成本低” 比极致性能更重要。

“在 Android 项目中,synchronized 是首选,简单可靠且 ART 已做大量优化;只有遇到 高并发读写、可中断、公平锁 等需求,才考虑 ReentrantLock 或 ReentrantReadWriteLock。”

http://www.xdnf.cn/news/1459783.html

相关文章:

  • Python学习3.0使用Unittest框架运行测试用例
  • 无人机防风技术难点解析
  • TDengine TIMETRUNCATE 函数用户使用手册
  • Netty从0到1系列之Buffer【下】
  • 2025年百度商业AI技术创新大赛赛道二:视频广告生成推理性能优化-初赛第五名,复赛第九名方案分享
  • JVM 运行时数据区域
  • java面试中经常会问到的dubbo问题有哪些(基础版)
  • JVM 类加载全过程
  • Node-RED服务成本/价格很高?那这不到“三张”的怎么说?
  • QT卡顿的可能原因
  • TP8 数组在模板html文件中输出json字符串格式{“0“:“x1“,“1“:“x2“,“2“:“x3“}
  • 在Spring MVC中使用查询字符串与参数
  • 2025市面上比较实用的财会行业证书,最值得考的8个职业证书推荐
  • 本地部署开源数据生成器项目实战指南
  • HarmonyOS应用开发之界面列表不刷新问题Bug排查记:从现象到解决完整记录
  • JS函数进阶
  • Roo Code之自定义指令(Custom Instructions),规则(Rules)
  • 硬盘分区格式化后产生了哪些变化
  • OpenStack VLAN网络类型实训案例
  • 机器学习:后篇
  • LangChain4j的初步学习【逐步添加中】
  • 强化学习DQN解决Cart_Pole问题
  • claude code route 使用教程|命令大全
  • linux中的awk使用详解
  • 深度解读《实施“人工智能+”行动的意见》:一场由场景、数据与价值链共同定义的产业升级
  • 【8】C#上位机---泛型、委托delegate与多线程Task
  • 2025年代理IP服务深度评测:三大平台横评,谁是最强业务助手?
  • 检查数据集格式(77)
  • 计算机二级C语言操作题(填空、修改、设计题)——真题库(16)附解析答案
  • C++基础——模板进阶