Java 锁的使用详解
Java 提供了多种锁机制来处理并发编程中的同步问题。下面我将通过代码示例来展示各种锁的使用方法和特点。
锁的选择指南
以下是选择合适锁的指南:
基本锁类型演示
// 由于这是在 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的CountDownLatchfinal 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中各种锁的使用方法和适用场景。