跳到主要内容

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();
}
}
}

适用场景

读写锁适合以下场景:

  1. 读多写少:读操作明显多于写操作
  2. 读操作耗时较长:允许并发读取提高性能
  3. 数据一致性要求高:写操作时需要排除所有读操作

不适合的场景:

  • 写操作频繁
  • 读操作很快(锁开销可能大于收益)
  • 需要精确控制读写的时序

StampedLock:高性能乐观锁

StampedLock的特点

StampedLock是Java 8引入的新的锁机制,提供了三种模式:

  1. 写锁:独占的,类似ReentrantWriteLock
  2. 悲观读锁:共享的,类似ReentrantReadLock
  3. 乐观读:无锁机制,性能最高

乐观读模式

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();
}
}

使用注意事项

  1. 不可重入:StampedLock不支持重入
  2. 忘记释放:如果忘记释放锁,可能导致死锁
  3. 复杂度高:使用时需要小心处理stamp值
  4. 适用场景:读多写少,对性能要求高的场景

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. 互斥条件:资源不能共享
  2. 请求与保持:持有资源的同时请求其他资源
  3. 不剥夺条件:不能强行剥夺已持有的资源
  4. 循环等待:存在等待链

预防死锁的方法:

// 方法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());
}
}

📚 总结

学习路径

  1. 基础理解:掌握synchronized的使用和原理
  2. 进阶学习:学习ReentrantLock的高级特性
  3. 场景优化:了解读写锁和StampedLock的适用场景
  4. 底层原理:理解AQS和LockSupport的工作机制
  5. 实践应用:在实际项目中合理选择和使用各种锁

最佳实践

  1. 锁的范围要最小化:只锁定必要的代码
  2. 避免嵌套锁:减少死锁风险
  3. 使用try-finally:确保锁一定会被释放
  4. 考虑公平性:根据场景选择公平或非公平锁
  5. 性能测试:在高并发场景下测试和优化

常见陷阱

  1. 忘记释放锁:总是使用try-finally
  2. 错误的锁对象:使用可变的对象作为锁
  3. 过度同步:锁的范围过大
  4. 错误的锁类型选择:没有根据场景选择合适的锁

记住,没有最好的锁,只有最合适的锁。理解每种锁的特点和适用场景,才能在实际开发中做出正确的选择。


参考资源