Java 中各种锁的使用详解

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中各种锁的使用方法和适用场景。

相关推荐
失散1319 分钟前
并发编程——17 CPU缓存架构详解&高性能内存队列Disruptor实战
java·缓存·架构·并发编程
only-qi5 小时前
146. LRU 缓存
java·算法·缓存
悟能不能悟5 小时前
js闭包问题
开发语言·前端·javascript
潼心1412o5 小时前
C语言(长期更新)第15讲 指针详解(五):习题实战
c语言·开发语言
xuxie136 小时前
SpringBoot文件下载(多文件以zip形式,单文件格式不变)
java·spring boot·后端
重生成为编程大王6 小时前
Java中的多态有什么用?
java·后端
Murphy_lx6 小时前
Lambda表达式
开发语言·c++
666和7776 小时前
Struts2 工作总结
java·数据库
中草药z6 小时前
【Stream API】高效简化集合处理
java·前端·javascript·stream·parallelstream·并行流
yangpipi-6 小时前
C++并发编程-23. 线程间切分任务的方法
开发语言·c++