Java锁机制完全指南
在多线程编程中,锁机制是保证线程安全的核心工具。当多个线程同时访问共享资源时,锁可以确保数据的一致性和完整性。本文将从基础概念开始,循序渐进地介绍Java中的各种锁机制,帮助你掌握并发编程的关键技能。
🔐 为什么要用锁?
现实中的问题
想象一个场景:银行账户有1000元,同时有两个操作:
- 线程A:取款100元
- 线程B:查询余额
如果没有锁保护,可能会出现以下问题:
public class BankAccount {
private int balance = 1000;
// 危险的操作!
public void withdraw(int amount) {
if (balance >= amount) {
// 假设在这里线程切换了...
try {
Thread.sleep(100); // 模拟处理时间
} catch (InterruptedException e) {}
balance -= amount;
System.out.println("取款成功:" + amount + "元,剩余:" + balance + "元");
} else {
System.out.println("余额不足");
}
}
public static void main(String[] args) {
BankAccount account = new BankAccount();
// 两个线程同时取款
new Thread(() -> account.withdraw(600)).start();
new Thread(() -> account.withdraw(600)).start();
}
}
可能输出:
取款成功:600元,剩余:400元
取款成功:600元,剩余:-200元
账户出现了负数!这就是典型的线程安全问题。
核心概念
1. 临界区(Critical Section)
访问共享资源的代码区域
2. 互斥(Mutual Exclusion)
同一时间只允许一个线程访问临界区
3. 原子性(Atomicity)
操作要么全部完成,要么都不执行
4. 可见性(Visibility)
一个线程的修改对其他线程可见
synchronized:Java内置锁
基本用法
synchronized是Java中最基础的同步机制,使用简单,语法简洁。
修饰实例方法
public class Counter {
private int count = 0;
// 锁住当前实例
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
修饰静态方法
public class StaticCounter {
private static int count = 0;
// 锁住Class对象
public static synchronized void increment() {
count++;
}
public static synchronized int getCount() {
return count;
}
}
修饰代码块
public class FlexibleCounter {
private int count = 0;
private final Object lock = new Object(); // 专门的锁对象
public void increment() {
synchronized (lock) { // 只锁定必要的代码
count++;
}
}
public void incrementWithThis() {
synchronized (this) { // 锁住当前对象
count++;
}
}
}
实际案例:线程安全的银行账户
public class SafeBankAccount {
private int balance;
public SafeBankAccount(int initialBalance) {
this.balance = initialBalance;
}
// 转账操作需要完整的事务保护
public synchronized boolean transfer(SafeBankAccount target, int amount) {
if (this.balance >= amount) {
this.balance -= amount;
// 注意:这里可能产生死锁!
target.deposit(amount);
return true;
}
return false;
}
public synchronized void deposit(int amount) {
this.balance += amount;
}
public synchronized int getBalance() {
return balance;
}
}
synchronized的工作原理
1. 对象头和Monitor
每个Java对象都有一个Monitor监视器:
- 对象头:存储锁信息
- Monitor:操作系统级的互斥量
2. 锁升级过程(JDK 1.6+优化)
无锁 → 偏向锁 → 轻量级锁 → 重量级锁
↑ ↑ ↑ ↑
│ │ │ │
线程A首次进入 → 同一线程重入 → 短时间竞争 → 长时间竞争
偏向锁:同一个线程重复获取锁时无需CAS操作 轻量级锁:自旋等待,避免OS切换 重量级锁:使用OS互斥量,线程阻塞
优点和缺点
优点
- ✅ 使用简单,语法简洁
- ✅ JVM自动管理锁的获取和释放
- ✅ JDK 1.6+性能优化明显
- ✅ 避免死锁(自动释放)
缺点
- ❌ 无法中断等待
- ❌ 无法设置超时
- ❌ 不支持公平锁
- ❌ 功能相对单一
ReentrantLock:可重入锁
为什么需要ReentrantLock?
synchronized功能有限,ReentrantLock提供了更强大的功能:
- 可中断的锁获取
- 可定时的锁获取
- 公平锁支持
- 多个条件变量
基本使用
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockCounter {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock(); // 获取锁
try {
count++;
} finally {
lock.unlock(); // 必须在finally中释放
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
高级特性
1. 可中断锁
public class InterruptibleLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void performTask() throws InterruptedException {
// 可以被中断的锁获取
lock.lockInterruptibly();
try {
// 执行耗时任务
Thread.sleep(5000);
System.out.println("任务完成");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
InterruptibleLockExample example = new InterruptibleLockExample();
Thread worker = new Thread(() -> {
try {
example.performTask();
} catch (InterruptedException e) {
System.out.println("线程被中断,退出任务");
}
});
worker.start();
// 主线程2秒后中断worker
try {
Thread.sleep(2000);
worker.interrupt();
} catch (InterruptedException e) {}
}
}
2. 尝试获取锁
public class TryLockExample {
private final ReentrantLock lock = new ReentrantLock();
public boolean performTaskWithTimeout() {
try {
// 尝试在3秒内获取锁
if (lock.tryLock(3, TimeUnit.SECONDS)) {
try {
// 执行任务
System.out.println("获取到锁,执行任务");
Thread.sleep(2000);
return true;
} finally {
lock.unlock();
}
} else {
System.out.println("超时未能获取锁");
return false;
}
} catch (InterruptedException e) {
System.out.println("被中断");
return false;
}
}
}
3. 公平锁
public class FairLockExample {
// 公平锁:按请求顺序获取锁
private final ReentrantLock fairLock = new ReentrantLock(true);
// 非公平锁(默认):可能插队,吞吐量更高
private final ReentrantLock unfairLock = new ReentrantLock(false);
public void demonstrateFairness() {
Runnable task = () -> {
for (int i = 0; i < 3; i++) {
fairLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 正在执行");
Thread.sleep(100);
} catch (InterruptedException e) {
// 处理中断
} finally {
fairLock.unlock();
}
}
};
// 创建多个线程测试公平性
for (int i = 0; i < 3; i++) {
new Thread(task, "Thread-" + i).start();
}
}
}
Condition条件变量
Condition提供了比Object的wait/notify更精确的线程控制:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class BoundedBuffer<T> {
private final T[] buffer;
private int head, tail, size;
private final ReentrantLock lock;
private final Condition notEmpty; // 缓冲区非空条件
private final Condition notFull; // 缓冲区非满条件
@SuppressWarnings("unchecked")
public BoundedBuffer(int capacity) {
buffer = (T[]) new Object[capacity];
lock = new ReentrantLock();
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
public void put(T item) throws InterruptedException {
lock.lock();
try {
// 等待缓冲区非满
while (size == buffer.length) {
System.out.println("缓冲区已满,等待消费者...");
notFull.await();
}
buffer[tail] = item;
tail = (tail + 1) % buffer.length;
size++;
// 通知消费者
System.out.println("生产了:" + item);
notEmpty.signal();
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
lock.lock();
try {
// 等待缓冲区非空
while (size == 0) {
System.out.println("缓冲区为空,等待生产者...");
notEmpty.await();
}
T item = buffer[head];
head = (head + 1) % buffer.length;
size--;
// 通知生产者
System.out.println("消费了:" + item);
notFull.signal();
return item;
} finally {
lock.unlock();
}
}
// 测试代码
public static void main(String[] args) {
BoundedBuffer<String> buffer = new BoundedBuffer<>(3);
// 生产者线程
new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
buffer.put("Item-" + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {}
}, "Producer").start();
// 消费者线程
new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
buffer.take();
Thread.sleep(2000);
}
} catch (InterruptedException e) {}
}, "Consumer").start();
}
}
ReadWriteLock:读写锁
读写分离的思想
在许多应用中,读操作远多于写操作。如果使用普通的互斥锁,即使是纯读操作也需要串行执行,造成不必要的性能损失。
读写锁允许多个线程同时读,但写操作是独占的。
基本使用
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteCache {
private final Map<String, String> cache = new HashMap<>();
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
// 读操作:可以并发执行
public String get(String key) {
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " 开始读取: " + key);
String value = cache.get(key);
System.out.println(Thread.currentThread().getName() + " 读取完成: " + value);
return value;
} finally {
rwLock.readLock().unlock();
}
}
// 写操作:独占执行
public void put(String key, String value) {
rwLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " 开始写入: " + key + "=" + value);
cache.put(key, value);
Thread.sleep(1000); // 模拟写操作耗时
System.out.println(Thread.currentThread().getName() + " 写入完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
rwLock.writeLock().unlock();
}
}
// 测试代码
public static void main(String[] args) throws InterruptedException {
ReadWriteCache cache = new ReadWriteCache();
// 初始化一些数据
cache.put("user1", "Alice");
cache.put("user2", "Bob");
// 启动多个读线程
for (int i = 0; i < 5; i++) {
new Thread(() -> {
while (true) {
cache.get("user1");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
break;
}
}
}, "Reader-" + i).start();
}
// 启动写线程
new Thread(() -> {
int counter = 3;
while (counter-- > 0) {
cache.put("user1", "Alice-" + System.currentTimeMillis());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
break;
}
}
}, "Writer").start();
}
}
锁降级
锁降级是指从写锁降级到读锁,这是支持的;但升级(从读锁升级到写锁)是不允许的。
public class LockDemotionExample {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private volatile String data;
public void updateAndRead() {
rwLock.writeLock().lock();
try {
// 获取写锁,修改数据
data = "Updated at " + System.currentTimeMillis();
// 在持有写锁的同时获取读锁(降级)
rwLock.readLock().lock();
try {
// 现在同时持有写锁和读锁
System.out.println("写锁降级为读锁,读取数据: " + data);
} finally {
// 释放读锁,但还持有写锁
rwLock.readLock().unlock();
}
} finally {
// 释放写锁
rwLock.writeLock().unlock();
}
}
// 错误示例:不能从读锁升级到写锁
public void badUpgrade() {
rwLock.readLock().lock();
try {
// 不能在持有读锁时获取写锁!会导致死锁
rwLock.writeLock().lock(); // 死锁!
try {
data = "New data";
} finally {
rwLock.writeLock().unlock();
}
} finally {
rwLock.readLock().unlock();
}
}
}
适用场景
读写锁适合以下场景:
- 读多写少:读操作明显多于写操作
- 读操作耗时较长:允许并发读取提高性能
- 数据一致性要求高:写操作时需要排除所有读操作
不适合的场景:
- 写操作频繁
- 读操作很快(锁开销可能大于收益)
- 需要精确控制读写的时序
StampedLock:高性能乐观锁
StampedLock的特点
StampedLock是Java 8引入的新的锁机制,提供了三种模式:
- 写锁:独占的,类似ReentrantWriteLock
- 悲观读锁:共享的,类似ReentrantReadLock
- 乐观读:无锁机制,性能最高
乐观读模式
import java.util.concurrent.locks.StampedLock;
public class StampedPoint {
private double x, y;
private final StampedLock sl = new StampedLock();
// 乐观读操作:无锁,性能最高
public double distanceFromOrigin() {
long stamp = sl.tryOptimisticRead(); // 获取乐观读标记
double currentX = x, currentY = y;
// 验证乐观读期间是否有写操作
if (!sl.validate(stamp)) {
// 如果有写操作,降级为悲观读
stamp = sl.readLock();
try {
currentX = x;
currentY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
// 悲观读操作:如果有写操作会阻塞
public void move(double deltaX, double deltaY) {
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
// 读写操作:从写锁降级到读锁
public void moveAndDistance(double deltaX, double deltaY) {
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
// 降级为读锁
stamp = sl.tryConvertToReadLock(stamp);
if (stamp != 0L) {
// 成功降级
try {
return Math.sqrt(x * x + y * y);
} finally {
sl.unlockRead(stamp);
}
} else {
// 降级失败,需要重新获取读锁
sl.unlockWrite(stamp);
stamp = sl.readLock();
try {
return Math.sqrt(x * x + y * y);
} finally {
sl.unlockRead(stamp);
}
}
} finally {
if (StampedLock.isWriteLockStamp(stamp)) {
sl.unlockWrite(stamp);
}
}
}
}
性能对比示例
public class LockPerformanceComparison {
private static final int READ_THREADS = 10;
private static final int WRITE_THREADS = 2;
private static final int OPERATIONS = 100000;
// 测试ReentrantReadWriteLock
public static void testReadWriteLock() throws InterruptedException {
ReadWriteLock rwLock = new ReentrantReadWriteLock();
AtomicInteger counter = new AtomicInteger(0);
CountDownLatch latch = new CountDownLatch(READ_THREADS + WRITE_THREADS);
long startTime = System.currentTimeMillis();
// 启动读线程
for (int i = 0; i < READ_THREADS; i++) {
new Thread(() -> {
for (int j = 0; j < OPERATIONS; j++) {
rwLock.readLock().lock();
try {
counter.get();
} finally {
rwLock.readLock().unlock();
}
}
latch.countDown();
}).start();
}
// 启动写线程
for (int i = 0; i < WRITE_THREADS; i++) {
new Thread(() -> {
for (int j = 0; j < OPERATIONS / 10; j++) {
rwLock.writeLock().lock();
try {
counter.incrementAndGet();
} finally {
rwLock.writeLock().unlock();
}
}
latch.countDown();
}).start();
}
latch.await();
System.out.println("ReadWriteLock 耗时: " + (System.currentTimeMillis() - startTime) + "ms");
}
// 测试StampedLock
public static void testStampedLock() throws InterruptedException {
StampedLock stampedLock = new StampedLock();
AtomicInteger counter = new AtomicInteger(0);
CountDownLatch latch = new CountDownLatch(READ_THREADS + WRITE_THREADS);
long startTime = System.currentTimeMillis();
// 启动读线程(使用乐观读)
for (int i = 0; i < READ_THREADS; i++) {
new Thread(() -> {
for (int j = 0; j < OPERATIONS; j++) {
long stamp = stampedLock.tryOptimisticRead();
int value = counter.get();
if (!stampedLock.validate(stamp)) {
stamp = stampedLock.readLock();
try {
value = counter.get();
} finally {
stampedLock.unlockRead(stamp);
}
}
}
latch.countDown();
}).start();
}
// 启动写线程
for (int i = 0; i < WRITE_THREADS; i++) {
new Thread(() -> {
for (int j = 0; j < OPERATIONS / 10; j++) {
long stamp = stampedLock.writeLock();
try {
counter.incrementAndGet();
} finally {
stampedLock.unlockWrite(stamp);
}
}
latch.countDown();
}).start();
}
latch.await();
System.out.println("StampedLock 耗时: " + (System.currentTimeMillis() - startTime) + "ms");
}
public static void main(String[] args) throws InterruptedException {
testReadWriteLock();
testStampedLock();
}
}
使用注意事项
- 不可重入:StampedLock不支持重入
- 忘记释放:如果忘记释放锁,可能导致死锁
- 复杂度高:使用时需要小心处理stamp值
- 适用场景:读多写少,对性能要求高的场景
LockSupport:底层工具类
LockSupport的作用
LockSupport是Java并发包中最基础的线程阻塞工具,其他锁机制都是基于它实现的。
基本用法
import java.util.concurrent.locks.LockSupport;
public class LockSupportExample {
public static void main(String[] args) throws InterruptedException {
Thread worker = new Thread(() -> {
System.out.println("Worker: 准备阻塞...");
LockSupport.park(); // 阻塞当前线程
System.out.println("Worker: 被唤醒,继续执行");
});
worker.start();
Thread.sleep(2000);
System.out.println("Main: 唤醒worker线程");
LockSupport.unpark(worker); // 唤醒指定线程
}
}
许可机制
LockSupport使用"许可"(permit)机制:
public class PermitExample {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
System.out.println("Thread1: 先unpark再park");
LockSupport.unpark(Thread.currentThread()); // 先给许可
System.out.println("Thread1: 获得许可,park不会阻塞");
LockSupport.park(); // 立即返回,因为已有许可
System.out.println("Thread1: 继续执行");
});
Thread thread2 = new Thread(() -> {
System.out.println("Thread2: 多次unpark");
LockSupport.unpark(Thread.currentThread());
LockSupport.unpark(Thread.currentThread()); // 第二次unpark无效
System.out.println("Thread2: 第一次park");
LockSupport.park(); // 立即返回
System.out.println("Thread2: 第二次park会阻塞");
LockSupport.park(); // 阻塞,因为没有许可了
System.out.println("Thread2: 这行不会执行");
});
thread1.start();
try {
thread1.join();
} catch (InterruptedException e) {}
thread2.start();
try {
Thread.sleep(2000);
LockSupport.unpark(thread2); // 解除thread2的阻塞
} catch (InterruptedException e) {}
}
}
与中断的交互
public class LockSupportInterruptExample {
public static void main(String[] args) throws InterruptedException {
Thread worker = new Thread(() -> {
System.out.println("Worker: 开始park");
LockSupport.park(); // 响应中断
System.out.println("Worker: park结束");
if (Thread.interrupted()) {
System.out.println("Worker: 检测到中断标志");
}
});
worker.start();
Thread.sleep(1000);
System.out.println("Main: 中断worker线程");
worker.interrupt(); // 中断会唤醒park的线程
}
}
实际应用:实现简单的阻塞队列
public class SimpleBlockingQueue<T> {
private final Queue<T> queue = new LinkedList<>();
private final Object lock = new Object();
public void put(T item) throws InterruptedException {
synchronized (lock) {
queue.add(item);
// 使用LockSupport唤醒等待的线程
LockSupport.unpark(Thread.currentThread());
}
}
public T take() throws InterruptedException {
synchronized (lock) {
while (queue.isEmpty()) {
// 简化版:实际实现需要更复杂的线程管理
LockSupport.parkNanos(1000000000L); // 等待1秒
}
return queue.poll();
}
}
}
📊 锁的选型指南
锁类型对比
| 锁类型 | 适用场景 | 优点 | 缺点 | 性能特点 |
|---|---|---|---|---|
| synchronized | 简单场景、低并发 | 使用简单、自动管理 | 功能有限、无法定制 | JDK1.6+优化良好 |
| ReentrantLock | 复杂同步需求 | 功能丰富、可定制 | 需要手动管理 | 中等性能 |
| ReadWriteLock | 读多写少 | 读写分离提高并发 | 写锁阻塞所有读 | 读操作并发好 |
| StampedLock | 高性能读多写少 | 乐观读性能极佳 | 复杂、不可重入 | 读操作性能最高 |
| LockSupport | 底层工具构建 | 灵活、底层控制 | 使用复杂 | 性能最好但需谨慎 |
选择建议
1. 简单场景
// 推荐:synchronized
public synchronized void increment() {
count++;
}
2. 需要超时或中断
// 推荐:ReentrantLock
if (lock.tryLock(5, TimeUnit.SECONDS)) {
try {
// 执行任务
} finally {
lock.unlock();
}
}
3. 读多写少场景
// 推荐:ReadWriteLock
public String readData() {
rwLock.readLock().lock();
try {
return data;
} finally {
rwLock.readLock().unlock();
}
}
4. 高性能读多写少
// 推荐:StampedLock
public String read() {
long stamp = sl.tryOptimisticRead();
String value = data;
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
value = data;
} finally {
sl.unlockRead(stamp);
}
}
return value;
}
🎯 面试重点与常见问题
核心面试题
1. synchronized vs ReentrantLock
问题: synchronized和ReentrantLock有什么区别?
答案要点:
// 对比表
synchronized:
- JVM关键字,自动加锁解锁
- 非公平锁,不可配置
- 不可中断等待
- 无超时机制
- 单个条件队列(等待集)
ReentrantLock:
- API类,手动加锁解锁
- 可选公平/非公平
- 可中断(lockInterruptibly)
- 支持超时(tryLock)
- 支持多个Condition
2. 什么是可重入锁?
答案: 同一线程可以重复获取同一个锁而不会死锁。
public class ReentrantExample {
private final Object lock = new Object();
public synchronized void methodA() {
methodB(); // 可以调用,因为synchronized是可重入的
}
public synchronized void methodB() {
System.out.println("重入锁示例");
}
}
3. 死锁的产生和预防
死锁的四个必要条件:
- 互斥条件:资源不能共享
- 请求与保持:持有资源的同时请求其他资源
- 不剥夺条件:不能强行剥夺已持有的资源
- 循环等待:存在等待链
预防死锁的方法:
// 方法1:统一的锁获取顺序
public class DeadlockPrevention {
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void method1() {
synchronized (lock1) {
synchronized (lock2) {
// 执行逻辑
}
}
}
public void method2() {
// 相同的锁获取顺序,避免死锁
synchronized (lock1) {
synchronized (lock2) {
// 执行逻辑
}
}
}
}
// 方法2:使用超时机制
public boolean transfer(Account from, Account to, int amount) {
while (true) {
if (from.lock.tryLock()) {
try {
if (to.lock.tryLock()) {
try {
from.withdraw(amount);
to.deposit(amount);
return true;
} finally {
to.lock.unlock();
}
}
} finally {
from.lock.unlock();
}
}
// 短暂休眠避免CPU浪费
Thread.sleep(10);
}
}
4. 读写锁的性能分析
问题: 读写锁一定比普通锁快吗?
答案: 不一定!取决于读写比例和操作复杂度。
// 写操作频繁的场景,读写锁可能更慢
public class WriteHeavyScenario {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final ReentrantLock lock = new ReentrantLock();
// 读写锁版本 - 写操作会阻塞所有读
public void operationWithRWLock() {
rwLock.writeLock().lock();
try {
// 写操作
} finally {
rwLock.writeLock().unlock();
}
}
// 普通锁版本 - 写操作只阻塞其他写
public void operationWithNormalLock() {
lock.lock();
try {
// 写操作
} finally {
lock.unlock();
}
}
}
5. AQS(AbstractQueuedSynchronizer)原理
核心概念:
- CLH队列:等待线程的FIFO队列
- state状态:同步器的状态变量
- exclusive/共享模式:独占或共享获取
// 简化的AQS使用示例
public class SimpleLock extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
public void lock() {
acquire(1);
}
public void unlock() {
release(1);
}
}
实际项目中的面试题
1. 高并发缓存设计
public class HighConcurrencyCache<K, V> {
private final Map<K, V> cache = new ConcurrentHashMap<>();
private final Map<K, Long> timestamps = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final long expireTime;
public HighConcurrencyCache(long expireTime, TimeUnit unit) {
this.expireTime = unit.toMillis(expireTime);
}
public V get(K key) {
rwLock.readLock().lock();
try {
Long timestamp = timestamps.get(key);
if (timestamp != null && System.currentTimeMillis() - timestamp < expireTime) {
return cache.get(key);
}
return null;
} finally {
rwLock.readLock().unlock();
}
}
public void put(K key, V value) {
rwLock.writeLock().lock();
try {
cache.put(key, value);
timestamps.put(key, System.currentTimeMillis());
} finally {
rwLock.writeLock().unlock();
}
}
// 定期清理过期数据
public void cleanExpired() {
rwLock.writeLock().lock();
try {
long now = System.currentTimeMillis();
Iterator<Map.Entry<K, Long>> iterator = timestamps.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<K, Long> entry = iterator.next();
if (now - entry.getValue() >= expireTime) {
K key = entry.getKey();
iterator.remove();
cache.remove(key);
}
}
} finally {
rwLock.writeLock().unlock();
}
}
}
2. 分布式锁的本地实现思路
public class LocalDistributedLock {
private final Map<String, Integer> lockCounters = new ConcurrentHashMap<>();
private final Map<String, Thread> lockOwners = new ConcurrentHashMap<>();
private final ReentrantLock globalLock = new ReentrantLock();
public boolean tryLock(String resource, long timeout, TimeUnit unit)
throws InterruptedException {
long deadline = System.nanoTime() + unit.toNanos(timeout);
while (System.nanoTime() < deadline) {
globalLock.lock();
try {
Thread currentOwner = lockOwners.get(resource);
if (currentOwner == null) {
// 资源未被锁定
lockOwners.put(resource, Thread.currentThread());
lockCounters.put(resource, 1);
return true;
} else if (currentOwner == Thread.currentThread()) {
// 可重入
lockCounters.merge(resource, 1, Integer::sum);
return true;
}
} finally {
globalLock.unlock();
}
// 短暂休眠
Thread.sleep(10);
}
return false;
}
public void unlock(String resource) {
globalLock.lock();
try {
if (lockOwners.get(resource) == Thread.currentThread()) {
int count = lockCounters.get(resource);
if (count == 1) {
lockOwners.remove(resource);
lockCounters.remove(resource);
} else {
lockCounters.put(resource, count - 1);
}
}
} finally {
globalLock.unlock();
}
}
}
🔧 调试和监控
死锁检测
public class DeadlockDetection {
public static void detectDeadlock() {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
if (deadlockedThreads != null) {
ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlockedThreads);
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("死锁线程: " + threadInfo.getThreadName());
System.out.println("等待锁: " + threadInfo.getLockName());
System.out.println("拥有锁: " + threadInfo.getLockOwnerName());
// 打印堆栈跟踪
for (StackTraceElement element : threadInfo.getStackTrace()) {
System.out.println("\t" + element);
}
}
}
}
public static void main(String[] args) {
// 启动死锁检测线程
new Thread(() -> {
while (true) {
detectDeadlock();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
break;
}
}
}).start();
}
}
锁竞争监控
public class LockContentionMonitor {
private final ReentrantLock lock = new ReentrantLock(true); // 公平锁便于监控
private final AtomicInteger contentionCount = new AtomicInteger(0);
public void monitorLock() {
if (lock.getQueueLength() > 0) {
System.out.println("锁竞争检测: " + lock.getQueueLength() + " 个线程在等待");
contentionCount.incrementAndGet();
}
}
public void criticalSection() {
monitorLock();
lock.lock();
try {
// 临界区代码
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
LockContentionMonitor monitor = new LockContentionMonitor();
// 创建多个线程模拟竞争
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 100; j++) {
monitor.criticalSection();
}
}).start();
}
Thread.sleep(10000);
System.out.println("总竞争次数: " + monitor.contentionCount.get());
}
}
📚 总结
学习路径
- 基础理解:掌握synchronized的使用和原理
- 进阶学习:学习ReentrantLock的高级特性
- 场景优化:了解读写锁和StampedLock的适用场景
- 底层原理:理解AQS和LockSupport的工作机制
- 实践应用:在实际项目中合理选择和使用各种锁
最佳实践
- 锁的范围要最小化:只锁定必要的代码
- 避免嵌套锁:减少死锁风险
- 使用try-finally:确保锁一定会被释放
- 考虑公平性:根据场景选择公平或非公平锁
- 性能测试:在高并发场景下测试和优化
常见陷阱
- 忘记释放锁:总是使用try-finally
- 错误的锁对象:使用可变的对象作为锁
- 过度同步:锁的范围过大
- 错误的锁类型选择:没有根据场景选择合适的锁
记住,没有最好的锁,只有最合适的锁。理解每种锁的特点和适用场景,才能在实际开发中做出正确的选择。