Java 锁的使用详解
Java 提供了多种锁机制来处理并发编程中的同步问题。下面我将通过代码示例来展示各种锁的使用方法和特点。
锁的选择指南
以下是选择合适锁的指南:

基本锁类型演示
javascript
// 由于这是在 Node.js 环境中模拟 Java 锁的概念,我们将使用注释和伪代码来解释
console.log("Java 锁的使用详解\n");
console.log("1. synchronized 关键字 (内置锁)");
console.log(`
public class SynchronizedExample {
private int count = 0;
// 同步方法 - 锁定当前对象实例
public synchronized void increment() {
count++;
}
// 同步代码块 - 锁定指定对象
public void incrementBlock() {
synchronized(this) {
count++;
}
}
// 静态同步方法 - 锁定类对象
public static synchronized void staticMethod() {
// 操作静态资源
}
}
`);
console.log("\n2. ReentrantLock (可重入锁)");
console.log(`
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
// 获取锁
lock.lock();
try {
count++;
} finally {
// 在 finally 块中释放锁,确保锁一定会被释放
lock.unlock();
}
}
// 尝试获取锁,带超时
public void incrementWithTimeout() {
try {
// 尝试在 1 秒内获取锁
boolean acquired = lock.tryLock(1, TimeUnit.SECONDS);
if (acquired) {
try {
count++;
} finally {
lock.unlock();
}
} else {
// 获取锁失败的处理
System.out.println("无法获取锁");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
`);
console.log("\n3. ReadWriteLock (读写锁)");
console.log(`
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private int data = 0;
// 读操作 - 可以多个线程同时读
public int readData() {
rwLock.readLock().lock();
try {
return data;
} finally {
rwLock.readLock().unlock();
}
}
// 写操作 - 只能一个线程写,且写时不能读
public void writeData(int newValue) {
rwLock.writeLock().lock();
try {
data = newValue;
} finally {
rwLock.writeLock().unlock();
}
}
}
`);
console.log("\n4. StampedLock (带版本号的锁)");
console.log(`
import java.util.concurrent.locks.StampedLock;
public class StampedLockExample {
private final StampedLock lock = new StampedLock();
private double x, y;
// 写操作 - 独占模式
public void move(double deltaX, double deltaY) {
long stamp = lock.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
lock.unlockWrite(stamp);
}
}
// 读操作 - 乐观读
public double distanceFromOrigin() {
// 乐观读,不阻塞写操作
long stamp = lock.tryOptimisticRead();
double currentX = x;
double currentY = y;
// 检查读取期间是否有写操作发生
if (!lock.validate(stamp)) {
// 有写操作,切换到悲观读锁
stamp = lock.readLock();
try {
currentX = x;
currentY = y;
} finally {
lock.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
// 悲观读锁
public double distanceFromOriginPessimistic() {
long stamp = lock.readLock();
try {
return Math.sqrt(x * x + y * y);
} finally {
lock.unlockRead(stamp);
}
}
}
`);
console.log("\n5. Condition (条件变量)");
console.log(`
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionExample {
private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final Object[] items = new Object[100];
private int putIndex, takeIndex, count;
// 生产者方法
public void put(Object x) throws InterruptedException {
lock.lock();
try {
// 缓冲区已满,等待
while (count == items.length) {
notFull.await();
}
items[putIndex] = x;
putIndex = (putIndex + 1) % items.length;
count++;
// 通知消费者有新数据
notEmpty.signal();
} finally {
lock.unlock();
}
}
// 消费者方法
public Object take() throws InterruptedException {
lock.lock();
try {
// 缓冲区为空,等待
while (count == 0) {
notEmpty.await();
}
Object x = items[takeIndex];
takeIndex = (takeIndex + 1) % items.length;
count--;
// 通知生产者有空间
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
`);
console.log("\n6. Semaphore (信号量)");
console.log(`
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
// 创建一个只允许5个线程同时访问的信号量
private final Semaphore semaphore = new Semaphore(5);
public void accessResource() {
try {
// 获取许可
semaphore.acquire();
try {
// 访问资源
System.out.println("访问资源中...");
Thread.sleep(1000); // 模拟资源访问
} finally {
// 释放许可
semaphore.release();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 尝试获取许可,不阻塞
public void tryAccessResource() {
boolean acquired = semaphore.tryAcquire();
if (acquired) {
try {
// 访问资源
System.out.println("访问资源中...");
} finally {
semaphore.release();
}
} else {
System.out.println("无法获取许可,资源繁忙");
}
}
}
`);
console.log("\n7. CountDownLatch (倒计时门闩)");
console.log(`
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public void execute() throws InterruptedException {
// 创建一个初始计数为3的CountDownLatch
final CountDownLatch latch = new CountDownLatch(3);
// 启动三个工作线程
for (int i = 0; i < 3; i++) {
final int taskId = i;
new Thread(() -> {
try {
// 模拟任务执行
System.out.println("任务 " + taskId + " 执行中...");
Thread.sleep(1000);
System.out.println("任务 " + taskId + " 完成");
// 计数减一
latch.countDown();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
// 主线程等待所有工作线程完成
System.out.println("等待所有任务完成...");
latch.await();
System.out.println("所有任务已完成,继续执行主线程");
}
}
`);
console.log("\n8. CyclicBarrier (循环栅栏)");
console.log(`
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public void execute() {
// 创建一个CyclicBarrier,当3个线程到达栅栏时,执行指定的任务
final CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("所有线程都到达栅栏,执行栅栏动作");
});
// 启动三个工作线程
for (int i = 0; i < 3; i++) {
final int taskId = i;
new Thread(() -> {
try {
System.out.println("线程 " + taskId + " 正在执行第一阶段");
Thread.sleep(1000);
// 第一个栅栏点
System.out.println("线程 " + taskId + " 到达第一个栅栏");
barrier.await();
System.out.println("线程 " + taskId + " 正在执行第二阶段");
Thread.sleep(1000);
// 第二个栅栏点 (循环使用)
System.out.println("线程 " + taskId + " 到达第二个栅栏");
barrier.await();
System.out.println("线程 " + taskId + " 完成所有工作");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
`);
console.log("\n9. Phaser (阶段器)");
console.log(`
import java.util.concurrent.Phaser;
public class PhaserExample {
public void execute() {
// 创建一个初始参与者数量为1的Phaser (主线程)
final Phaser phaser = new Phaser(1);
// 创建并启动3个工作线程
for (int i = 0; i < 3; i++) {
final int taskId = i;
// 注册一个参与者
phaser.register();
new Thread(() -> {
try {
System.out.println("线程 " + taskId + " 开始第一阶段");
Thread.sleep(1000);
// 到达第一阶段结束点,等待其他线程
System.out.println("线程 " + taskId + " 完成第一阶段");
phaser.arriveAndAwaitAdvance();
System.out.println("线程 " + taskId + " 开始第二阶段");
Thread.sleep(1000);
// 到达第二阶段结束点,等待其他线程
System.out.println("线程 " + taskId + " 完成第二阶段");
phaser.arriveAndAwaitAdvance();
System.out.println("线程 " + taskId + " 开始第三阶段");
Thread.sleep(1000);
// 完成所有阶段,取消注册
System.out.println("线程 " + taskId + " 完成所有阶段");
phaser.arriveAndDeregister();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
// 主线程等待所有工作线程完成第一阶段
phaser.arriveAndAwaitAdvance();
System.out.println("所有线程完成第一阶段");
// 主线程等待所有工作线程完成第二阶段
phaser.arriveAndAwaitAdvance();
System.out.println("所有线程完成第二阶段");
// 主线程等待所有工作线程完成第三阶段
phaser.arriveAndAwaitAdvance();
System.out.println("所有线程完成所有阶段");
// 主线程取消注册
phaser.arriveAndDeregister();
}
}
`);
console.log("\n10. 锁的最佳实践");
console.log(`
1. 尽量缩小同步范围,只锁定必要的代码块
2. 避免在持有锁的情况下执行耗时操作
3. 避免死锁:
- 按固定顺序获取多个锁
- 使用带超时的锁获取方法
- 使用 tryLock() 方法避免死锁
4. 优先使用并发集合而不是同步集合
5. 考虑使用无锁数据结构和原子变量
6. 适当选择锁类型:
- 简单场景:synchronized
- 需要高级特性:ReentrantLock
- 读多写少:ReadWriteLock
- 高并发读场景:StampedLock
`);
各种锁的特点比较
锁类型 | 可重入 | 公平性选择 | 阻塞 | 超时等待 | 可中断 | 适用场景 |
---|---|---|---|---|---|---|
synchronized | 是 | 否 | 是 | 否 | 否 | 简单同步场景 |
ReentrantLock | 是 | 是 | 是 | 是 | 是 | 需要高级特性的场景 |
ReadWriteLock | 是 | 是 | 是 | 是 | 是 | 读多写少场景 |
StampedLock | 否 | 否 | 部分 | 是 | 是 | 高性能读多写少场景 |
Semaphore | 否 | 是 | 是 | 是 | 是 | 资源数量控制 |
CountDownLatch | 否 | 否 | 是 | 是 | 是 | 等待多个线程完成 |
CyclicBarrier | 否 | 否 | 是 | 是 | 是 | 多个线程相互等待 |
Phaser | 否 | 否 | 是 | 否 | 是 | 分阶段任务协调 |
希望这些示例和说明能帮助您理解Java中各种锁的使用方法和适用场景。