Bootstrap

Java基础夯实——2.6 Java中的锁

Java 中,锁(Lock)是用于控制多线程并发访问共享资源的工具。它可以确保在同一时刻只有一个线程能够访问共享资源,从而避免数据不一致的问题。

1. 内置锁(synchronized 关键字)

Java 提供了内置锁,也称为监视器锁,这是最简单和常用的锁机制。

  • 同步方法:锁住整个方法,锁的对象是 this(实例锁)或类对象(类锁)。
    public synchronized void exampleMethod() {
        // 临界区代码
    }
    
  • 同步代码块:锁定指定对象,粒度更细。
    public void exampleMethod() {
        synchronized (this) {
            // 临界区代码
        }
    }
    

特点

  • 自动获取和释放锁。
  • 简单易用,但可能导致线程阻塞。
  • 不支持尝试获取锁、超时获取锁或中断锁等高级功能。

2. 显式锁(java.util.concurrent.locks.Lock 接口)

显式锁提供了更灵活的锁定机制,允许显式获取和释放锁。

主要实现类

  1. ReentrantLock(可重入锁)

    • 支持公平锁和非公平锁。
    • 允许尝试获取锁、超时获取锁或中断锁。
    Lock lock = new ReentrantLock();
    try {
        lock.lock(); // 获取锁
        // 临界区代码
    } finally {
        lock.unlock(); // 确保释放锁
    }
    
  2. ReentrantReadWriteLock(读写锁)

    • 提供读锁(共享锁)和写锁(独占锁),适合读多写少的场景。
    • 读线程可以并发,写线程则独占。
    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    lock.readLock().lock(); // 读操作
    lock.writeLock().lock(); // 写操作
    

特点

  • 更灵活,支持多种高级功能。
  • 使用时需要手动管理锁的获取和释放,容易引发死锁。
  • 性能可能优于 synchronized,尤其是在高并发情况下。

3. StampedLock

  • StampedLock 是一种改进的读写锁,提供了乐观读锁(Optimistic Read),适合读多写少的场景。

  • ReentrantReadWriteLock 不同,StampedLock 不是可重入的。

    StampedLock lock = new StampedLock();
    long stamp = lock.readLock();
    try {
        // 读操作
    } finally {
        lock.unlockRead(stamp);
    }
    

4. 信号量(Semaphore)

  • Semaphore 用于控制同时访问某个资源的线程数量。

  • 常用于限制连接数或资源池的并发访问。

    Semaphore semaphore = new Semaphore(3); // 最大并发数为3
    try {
        semaphore.acquire(); // 获取许可
        // 临界区代码
    } finally {
        semaphore.release(); // 释放许可
    }
    

5. 偏向锁、轻量级锁和重量级锁

  • Java 虚拟机(JVM)底层对 synchronized 的实现进行了优化,包括偏向锁、轻量级锁和重量级锁。
  • 这些锁的选择由 JVM 根据竞争情况动态决定,以提高性能。

比较与选择

特性synchronizedReentrantLockStampedLock
使用难度简单较复杂较复杂
性能较低较高较高
可重入性
读写锁支持支持(需额外类)支持
手动管理锁
;