Files
Knowledge/0 编程/java/ReentrantLock 常见用法.md
T

6.9 KiB
Raw Blame History

ReentrantLock 是 Java 并发包中提供的可重入锁,比 synchronized 更灵活。以下是其常见用法:

1. 基础锁使用

import java.util.concurrent.locks.ReentrantLock;

public class BasicUsage {
    private final ReentrantLock lock = new ReentrantLock();
    private int count = 0;

    public void increment() {
        lock.lock();  // 获取锁
        try {
            count++;
            System.out.println("Count: " + count);
        } finally {
            lock.unlock();  // 必须放在 finally 中确保释放锁
        }
    }
}

2. 尝试获取锁(非阻塞)

public class TryLockExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void tryLockMethod() {
        if (lock.tryLock()) {  // 尝试获取锁,立即返回结果
            try {
                // 获取锁成功,执行临界区代码
                System.out.println("获取锁成功");
            } finally {
                lock.unlock();
            }
        } else {
            // 获取锁失败,执行其他逻辑
            System.out.println("获取锁失败,执行其他操作");
        }
    }
}

3. 带超时的尝试获取锁

import java.util.concurrent.TimeUnit;

public class TimeoutLockExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void timeoutLockMethod() throws InterruptedException {
        // 尝试在指定时间内获取锁
        if (lock.tryLock(1, TimeUnit.SECONDS)) {
            try {
                System.out.println("在1秒内获取到锁");
                Thread.sleep(2000);  // 模拟业务操作
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println("获取锁超时");
        }
    }
}

4. 可中断的锁获取

public class InterruptibleLockExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void interruptibleMethod() throws InterruptedException {
        lock.lockInterruptibly();  // 可响应中断的锁获取
        try {
            // 长时间操作
            while (true) {
                // 模拟长时间任务
                if (Thread.currentThread().isInterrupted()) {
                    break;
                }
            }
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}

5. 公平锁与非公平锁

public class FairLockExample {
    // 创建公平锁(按申请顺序获取锁)
    private final ReentrantLock fairLock = new ReentrantLock(true);
    
    // 创建非公平锁(默认,性能更高)
    private final ReentrantLock unfairLock = new ReentrantLock();
}

6. 条件变量(Condition)

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionExample {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private boolean conditionMet = false;

    public void awaitCondition() throws InterruptedException {
        lock.lock();
        try {
            while (!conditionMet) {
                condition.await();  // 释放锁并等待
            }
            // 条件满足,执行后续操作
            System.out.println("条件满足,继续执行");
        } finally {
            lock.unlock();
        }
    }

    public void signalCondition() {
        lock.lock();
        try {
            conditionMet = true;
            condition.signalAll();  // 唤醒所有等待线程
            // 或使用 condition.signal() 唤醒一个线程
        } finally {
            lock.unlock();
        }
    }
}

7. 锁的查询方法

public class LockStatusExample {
    private final ReentrantLock lock = new ReentrantLock();

    public void checkLockStatus() {
        System.out.println("是否被当前线程持有: " + lock.isHeldByCurrentThread());
        System.out.println("是否被锁定: " + lock.isLocked());
        System.out.println("等待队列长度: " + lock.getQueueLength());
        System.out.println("当前线程持有锁的次数: " + lock.getHoldCount());
        
        // 获取锁信息
        if (lock.isLocked()) {
            System.out.println("持有锁的线程: " + 
                (lock.getOwner() != null ? lock.getOwner().getName() : "null"));
        }
    }
}

8. 综合示例:线程安全的队列

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class BlockingQueue<T> {
    private final Queue<T> queue = new LinkedList<>();
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notEmpty = lock.newCondition();
    private final Condition notFull = lock.newCondition();
    private final int capacity;

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
    }

    public void put(T item) throws InterruptedException {
        lock.lock();
        try {
            while (queue.size() == capacity) {
                notFull.await();  // 队列满,等待
            }
            queue.offer(item);
            notEmpty.signal();  // 唤醒等待获取的线程
        } finally {
            lock.unlock();
        }
    }

    public T take() throws InterruptedException {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                notEmpty.await();  // 队列空,等待
            }
            T item = queue.poll();
            notFull.signal();  // 唤醒等待放入的线程
            return item;
        } finally {
            lock.unlock();
        }
    }

    public int size() {
        lock.lock();
        try {
            return queue.size();
        } finally {
            lock.unlock();
        }
    }
}

9. 与 synchronized 对比

public class Comparison {
    // synchronized 方式
    public synchronized void syncMethod() {
        // 临界区代码
    }
    
    // ReentrantLock 方式
    private final ReentrantLock lock = new ReentrantLock();
    
    public void lockMethod() {
        lock.lock();
        try {
            // 临界区代码
        } finally {
            lock.unlock();
        }
    }
}

最佳实践

  1. 始终在 finally 块中释放锁,防止异常导致锁无法释放
  2. 不要嵌套过多 tryLock,避免代码复杂度增加
  3. 公平锁适用于等待时间差异大的场景,但性能较低
  4. 考虑使用 tryLock 避免死锁
  5. 合理使用 Condition 替代 Object 的 wait/notify

选择建议

  • 简单同步场景:优先使用 synchronized
  • 需要高级功能(超时、可中断、公平锁):使用 ReentrantLock
  • 需要多个等待条件:使用 Condition
  • 性能敏感场景:测试后选择(通常 synchronized 性能更好)