问题引入:多线程环境下的资源竞争
public class Main3 {
public static void main(String[] args) {
// 创建资源类
ticket ticket = new ticket();
new Thread(() ->{
for (int i = 0; i < 50; i++) {
ticket.sale();
}
}, "线程1").start();
new Thread(() ->{
for (int i = 0; i < 50; i++) {
ticket.sale();
}
}, "线程2").start();
new Thread(() ->{
for (int i = 0; i < 50; i++) {
ticket.sale();
}
}, "线程3").start();
}
}
// 资源类(票)
class ticket {
int ticketNum = 50;
// 卖票方法
void sale () {
if (ticketNum > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + ticketNum-- + "张票");
}
}
}
当三个线程并发执行售票操作时,会出现以下问题:
-
超卖(卖出超过库存数量的票)
-
重复售票(同一票号被多次卖出)
-
票号跳跃(打印顺序不符合预期)
未加锁前,多个线程卖票存在支援抢占
线程1卖出了第50张票
线程1卖出了第49张票
线程1卖出了第48张票
.............
线程2卖出了第1张票
线程1卖出了第11张票
线程3卖出了第25张票
Lock锁
Lock是一个接口
锁分为公平锁和非公平锁
公平锁:所有线程排队执行,不能插队
非公平锁:线程可以插队,cpu根据实际情况最大化提高效率
ReentrantLock默认是非公平锁
// 不传参数,默认非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
// 传入fair,如果是true则是公平锁,如果是false则是非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
二、同步方案对比
2.1 synchronized 实现方案
// 线程资源类(问题代码)
// 资源类(票)
class ticket {
int ticketNum = 50;
// 卖票方法
synchronized void sale () {
if (ticketNum > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + ticketNum-- + "张票");
}
}
}
2.2 Lock 实现方案
// 资源类(票)
class Ticket1 {
int ticketNum = 50;
// 默认
Lock lock = new ReentrantLock();
// 卖票方法
synchronized void sale () {
lock.lock();
if (ticketNum > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + ticketNum-- + "张票");
}
lock.unlock();
}
}
三、核心差异深度解析
3.1 实现层级
特性 | synchronized | Lock |
---|---|---|
实现方式 | JVM 内置关键字 | Java API 接口 |
类结构 | 自动关联对象监视器 | 需要显式实例化锁对象 |
字节码实现 | 通过 monitorenter/monitorexit 指令 | 通过 AQS 队列同步器实现 |
3.2 锁管理机制
特性 | synchronized | Lock |
---|---|---|
锁获取 | 自动获取和释放 | 必须显式调用 lock()/unlock() |
异常处理 | 自动释放锁 | 需在 finally 块中确保释放 |
死锁风险 | 无显式释放问题 | 忘记 unlock() 会导致死锁 |
3.3 功能特性对比
特性 | synchronized | Lock |
---|---|---|
可中断性 | 不支持 | 支持 lockInterruptibly() |
超时机制 | 不支持 | 支持 tryLock(long, TimeUnit) |
公平性 | 仅非公平锁 | 支持公平/非公平策略 |
条件变量 | 通过 wait()/notify() | 支持多个 Condition 对象 |
锁状态查询 | 不支持 | 提供 isLocked() 等方法 |
3.4 性能表现
-
Java 6 前:synchronized 性能显著低于 Lock
-
Java 6+:引入锁升级机制后性能差距缩小
-
适用场景:
-
低竞争:优先 synchronized(JVM 自动优化)
-
高竞争:考虑使用 Lock 的精细化控制
-
如何选择
推荐使用 synchronized 的场景:
-
简单的同步代码块
-
方法级别的同步控制
-
不需要高级锁特性的情况
-
资源竞争不激烈的场景
推荐使用 Lock 的场景:
-
需要可中断的锁获取机制
-
要求超时获取锁的功能
-
需要多个条件变量的复杂同步
-
需要公平锁策略
-
需要获取锁状态信息
-
细粒度的同步控制需求