文档概述
本文档系统对比 JVM 生态中三种核心互斥锁机制:synchronized(JVM 内置线程锁)、java.util.concurrent.locks.ReentrantLock(Java 显式线程锁)、kotlinx.coroutines.sync.Mutex(Kotlin 协程锁),明确三者核心定位、特性差异、选型逻辑与实操规范。
一、核心概念与定位
| 锁类型 | 核心定位 | 所属体系 | 核心设计目标 |
|---|---|---|---|
synchronized |
JVM原生内置隐式锁,无需手动释放 | Java 线程模型 | 极简实现线程级临界区互斥 |
ReentrantLock |
Java显式可重入锁,支持灵活高级特性 | Java 线程模型 | 弥补synchronized功能局限,适配复杂线程并发 |
Mutex |
Kotlin协程专属互斥锁,挂起而非阻塞 | Kotlin 协程模型 | 保留协程轻量级优势,实现协程级临界区互斥 |
核心本质区分 :synchronized/ReentrantLock 是线程级阻塞锁 ,加锁失败会阻塞整个线程,触发系统级上下文切换;Mutex 是协程级挂起锁,加锁失败仅挂起当前协程,线程可继续处理其他协程,资源利用率更高。
二、核心特性对比
| 特性维度 | synchronized |
ReentrantLock |
Mutex |
|---|---|---|---|
| 锁释放方式 | 隐式自动释放(JVM保证) | 显式手动lock/unlock | withLock自动释放,手动lock需finally解锁 |
| 公平锁支持 | 仅非公平,不可配置 | 支持,构造器可指定 | 支持,构造器可指定 |
| 超时/中断能力 | 不支持 | 支持超时获取、可中断加锁 | 支持超时获取,响应协程取消 |
| 重入性 | 支持 | 支持 | 支持 |
| 协程适配性 | 极差,阻塞线程废掉协程优势 | 差,同样阻塞线程 | 完美适配,纯挂起无阻塞 |
| 跨线程/协程使用 | 纯线程场景可用 | 全场景通用 | 非协程侧需runBlocking桥接 |
三、典型用法示例
1. synchronized 基础用法(Java)
java
public class SynchronizedDemo {
private int counter = 0;
public void increment() {
// 隐式加锁,代码块执行完毕自动释放
synchronized (this) {
counter++;
System.out.println("当前值:" + counter);
}
}
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
// 多线程并发调用
for (int i = 0; i < 5; i++) {
new Thread(demo::increment).start();
}
}
}
2. ReentrantLock 基础用法(Java,异常安全)
java
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
private int counter = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
counter++;
} finally {
lock.unlock(); // 必须finally释放,避免死锁
}
}
}
3. Mutex 基础用法(Kotlin协程)
kotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
var counter = 0
val mutex = Mutex()
fun main() = runBlocking {
repeat(5) {
launch(Dispatchers.Default) {
// 自动加锁+解锁,异常安全,推荐写法
mutex.withLock {
counter++
delay(10) // 协程挂起,线程不阻塞
}
}
}
}
4. 跨协程+线程混用(Mutex+runBlocking)
kotlin
// 非协程线程用runBlocking桥接,阻塞代价极低
Thread {
Thread.sleep(100)
runBlocking { mutex.withLock { counter++ } }
}.start()
四、总结
(一)选型
-
纯Java多线程,简单短临界区 :选synchronized,代码极简,JVM自动管理,无额外心智负担。
-
纯Java多线程,复杂需求 (超时、中断、公平锁):选ReentrantLock,功能灵活,可控性强。
-
Kotlin协程专属场景 :必选Mutex,完全适配协程,保留轻量级核心优势。
-
协程+普通线程混合场景:临界区极短用Mutex+runBlocking;临界区较长直接用ReentrantLock,避免主线程阻塞。
(二)高频避坑与强制实践
-
禁止协程内用synchronized/ReentrantLock:会阻塞线程,彻底丢失协程轻量级并发优势,属于核心禁忌。
-
显式锁必须finally解锁:ReentrantLock和Mutex手动调用lock时,务必在finally块解锁,杜绝死锁。
-
Mutex禁止非协程直接调用:挂起函数只能在协程内执行,非协程侧必须用runBlocking包裹。
-
锁粒度尽量细:只包裹共享资源操作代码,缩短锁持有时间,提升并发效率。
-
慎用公平锁:公平锁性能更低,仅在严格要求执行顺序时使用,默认用非公平锁即可。
线程并发看场景:简单用synchronized,复杂用ReentrantLock;协程并发只选Mutex,混合场景按需桥接;所有显式锁牢记"加锁必解锁",守住并发安全底线。