JUC解析-ReentrantLock

本文详细介绍了AQS中ReentrantLock和ReentrantReadWriteLock的实现原理,包括公平锁与非公平锁的区别,以及如何实现加锁、解锁等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AQS在JUC中存在两种常用的锁实现ReentrantLock和ReentrantReadWriteLock。

  • ReentrantLock是互斥锁的实现,其中包括公平锁和非公平锁两种模式。
  • ReentrantReadWriteLock是读写锁的实现,其中包括读锁和写锁两种锁。

先介绍下锁的两种模式:

  • 公平锁 : 线程按照他们发出请求的顺序获取锁。
  • 非公平锁 : 当一个线程请求非公平锁时,如果在发出请求的同时该锁变成可用状态,那么这个线程会跳过队列中所有的等待线程而获得锁,也就是说非公平锁允许插队的方式获取锁。

非公平锁有着更高的效率,也是ReentrantLock默认的实现方式,之所以效率高是因为堵塞在队列中的线程获取锁的过程是需要一定的延迟,而这一段延迟时间可以通过被抢断而将锁提供其他的线程工作。

ReentrantLock

首先看下ReentrantLock需要实现的功能:

  • 加锁
  • 解锁
  • 创建信号量

具体的定义如下:

public interface Lock {
    //获取锁,等待过程中忽略中断
    void lock();
    //在等待获取锁的同时可以影响中断
    void lockInterruptibly() throws InterruptedException;
    //尝试获取锁,会立即返回,True获取成功,False获取失败
    boolean tryLock();
    //尝试获取锁,等待时间为unit.toNanos(time)方式,等待的过程中可以响应中断
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    //释放锁资源
    void unlock();
    //创建信号量
    Condition newCondition();
}
复制代码

说明 : 面向接口的编程,通用的功能通过实现接口的方式实现。

实现方式

ReentrantLock只包含一个成员类Sync,ReentrantLock的功能都是通过该成员实现的,

  • Sync是AQS的抽象子类。
  • Sync的具体实现子类包括NonfairSync和FairSync。
  • ReentrantLock默认非公平的实现方式。

lock实现

非公平锁实现
  final void lock() {
    if (compareAndSetState(0, 1))
      setExclusiveOwnerThread(Thread.currentThread());
    else
      acquire(1);
  }
复制代码
  1. 设置state状态,如果设置成功,则表示获取锁成功,设置锁占有线程为本线程,此时该线程就跳过队列中排队的其他线程,优先获取资源锁,这就是非公平的实现。
  2. 如果获取失败,则调用AQS中的acquire(1)去获取锁,回顾下AQS中acquire(1)的定义:
  public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
      acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
      selfInterrupt();
  }
复制代码
  1. 在该类中tryAcquire的具体实现为:
  protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
  }
  
  final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    //获取锁的状态,定义在AQS中
    int c = getState();
    if (c == 0) {
      if (compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(current);
        return true;
      }
    }
    else if (current == getExclusiveOwnerThread()) {
      int nextc = c + acquires;
      if (nextc < 0) // overflow
        throw new Error("Maximum lock count exceeded");
      setState(nextc);
      return true;
    }
    return false;
  }
复制代码

说明:

  • 首先获取锁的当前状态,如果锁未被占用,则调用compareAndSetState配置锁的状态,配置成功则返回True
  • 如果锁已经被占有,并且占有锁的线程为本调用线程,则修改state的状态,并且返回true,这就是锁的可重入性
  • 否则返回False
公平锁实现

公平锁实现是直接调用AQS中的acquire实现,具体:

  final void lock() {
    acquire(1);
  }
  
  protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
      if (!hasQueuedPredecessors() &&
        compareAndSetState(0, acquires)) {
          setExclusiveOwnerThread(current);
          return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
      int nextc = c + acquires;
      if (nextc < 0)
        throw new Error("Maximum lock count exceeded");
      setState(nextc);
      return true;
    }
    return false;
  }
复制代码

说明:

  • 如果当前锁未被占有,则判断AQS中的堵塞队列中是否有等待的线程,如果没有,则尝试获取锁,如果获取成功,修改锁的持有者状态,并且返回true。
  • 否则如果AQS等待队列中已经存在等待的线程,则判断持有锁的时候是本线程,如果是,则修改state的状态,实现锁的可重入性,返回true。
  • 否则返回False

下面是hasQueuedPredecessors的具体实现:

  public final boolean hasQueuedPredecessors() {
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    //队列不为空 && 头结点的后继节点不为空 && 后继节点线程不为执行线程
    return h != t &&
          ((s = h.next) == null || s.thread != Thread.currentThread());
    }

复制代码

unlock实现

对于unlock的实现,在公平模式和非公平模式下都是相同的,具体:

  public void unlock() {
        sync.release(1);
  }
  
  protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
      throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
      free = true;
      setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
  }
复制代码

说明:

  • 如果持有锁的线程不是本线程,则抛出IllegalMonitorStateException异常
  • 计算锁释放后的状态,如果为0表示全部释放,并且修改锁持有的线程为null,返回true,否则只修改锁的状态,返回false,这里不用使用CAS设置state,因为此时线程是获取锁的。

信号量实现

实现比较简单,直接上代码:

  public Condition newCondition() {
    return sync.newCondition();
  }
    
  final ConditionObject newCondition() {
    return new ConditionObject();
  }
复制代码

完。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值