ReentrantLock-可重入锁

ReentrantLock是Java并发包java.util.concurrent.locks中的一个类,它实现了Lock接口,提供了一种与Synchronized方法和语句相同的基本行为和语义的互斥锁,但具有更多的扩展功能。

主要特点

  • 可重入性
    与 synchronized 关键字一样,ReentrantLock 允许同一个线程多次获得锁,而不会发生死锁。每次成功获取锁后,锁的持有计数会增加,每次释放锁时,计数会减少。只有当计数为0时,锁才完全释放,其他线程才能获取该锁。
  • 公平性
    ReentrantLock 构造函数接受一个可选的 boolean 类型的 fairness 参数。如果设置为 true,则锁将按照请求锁的顺序(即等待时间最长的线程将优先获得锁),来授予访问权限。这有助于减少饥饿现象,但可能会降低吞吐量。如果设置为 false(默认值),则不保证任何特定的访问顺序。
  • 尝试非阻塞地获取锁
    ReentrantLock 提供了 tryLock() 方法,该方法尝试获取锁,如果锁可用,则立即返回 true,否则立即返回 false。这与 synchronized 关键字不同,后者在锁不可用时会使线程阻塞。
  • 可中断的锁获取
    ReentrantLock 支持可中断的锁获取操作,即线程在等待锁的过程中可以被中断。这通过 lockInterruptibly() 方法实现,如果当前线程在等待锁的过程中被中断,则会抛出 InterruptedException。
  • 条件变量
    与 synchronized 关键字不同,ReentrantLock 提供了条件变量(Condition),允许多个条件等待集。这允许更灵活的线程同步控制。

使用建议

  • 防止死锁
    使用try-finally语句,在调用lock()方法后,应立即使用try-finally语句来确保锁最终会被释放;
  • 考虑公平性
    如果实际应用场景中,锁的获取顺序很重要,或者希望减少饥饿现象,可以考虑将fairness参数设置为true。但请注意,这可能会降低系统的吞吐量。
  • 避免过度使用
    虽然 ReentrantLock提供了比synchronized更多的功能,但过度使用可能会使代码变得复杂且难以维护。在大多数情况下,synchronized 关键字已经足够使用。

示例代码

class ReentrantLockSample {
    // 默认非公平锁
    private final ReentrantLock lock = new ReentrantLock();

    // 公平锁
    // private final ReentrantLock lock = new ReentrantLock(true);  
  
    public void method() {
        // 尝试获取锁  
        lock.lock();
        try {  
            // ... 方法体  
        } finally {  
            // 释放锁  
            lock.unlock(); 
        }  
    }  
}

在这个例子中,lock.lock() 尝试获取锁,如果锁被其他线程持有,则当前线程将阻塞,直到锁被释放。在 try 块中执行的方法体完成后,无论是否发生异常,finally 块都会确保锁被释放。

获取锁的过程

获取锁有两种方式:lock()或者tryLock()。lock()方法会根据获取锁策略(公平与否)去获取锁;tryLock()则是想要立即获取锁,所以无论是哪种获取锁策略都是通过非公平方式获取锁。

  • 公平锁(先到先得)
    获取锁的state状态,如果为0则尝试获取锁;如果已有其他线程获取到锁则加入到队尾等待;
protected final boolean tryAcquire(int acquires) {
  final Thread current = Thread.currentThread();
  int c = getState();
  if (c == 0) {
    // 1.首先查询是否有比当前线程获取锁等待时间更长的线程
    // 2.更新状态-获取成功
    if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
      setExclusiveOwnerThread(current);
      return true;
    }
  }
  // 重入锁获取
  else if (current == getExclusiveOwnerThread()) {
    int nextc = c + acquires;
    // 最大重入次数为int的最大值
    if (nextc < 0){
      throw new Error("Maximum lock count exceeded");
    }
    setState(nextc);
    return true;
  }
  return false;
}        
  • 非公平锁(随机)
    直接尝试获取锁,获取成功则将当前线程设置独占;获取失败则通过公平锁获取方式重新获取;
final void lock() {
  if (compareAndSetState(0, 1)){
    setExclusiveOwnerThread(Thread.currentThread());
  }else{
    acquire(1);
  }
}
protected final boolean tryAcquire(int acquires) {
  final Thread current = Thread.currentThread();
  int c = getState();
  if (c == 0) {
    // 更新状态-获取成功
    if (compareAndSetState(0, acquires)) {
      setExclusiveOwnerThread(current);
      return true;
    }
  }
  // 重入锁获取
  else if (current == getExclusiveOwnerThread()) {
    int nextc = c + acquires;
    // 最大重入次数为int的最大值
    if (nextc < 0){
      throw new Error("Maximum lock count exceeded");
    }
    setState(nextc);
    return true;
  }
  return false;
}

img
img

posted @ 2024-09-09 14:23  BugsHunter  阅读(14)  评论(0编辑  收藏  举报