ReentrantLock-学习

189 阅读1分钟

简介

ReentrantLock 用于控制资源的访问;

ReentrantLock是独占锁,且支持公平锁与非公平锁;

ReentrantLock实现Lock接口,实现加锁、解锁 接口规范;

内部类Sync继承AQS,实现了获取、释放资源逻辑

基本成员

  • Sync
  • FairSync
  • NonFairSync

Sync

描述:

同步对象,ReentrantLock的内部类,继承AQS,实现独占锁对应方法:tryAcquire、tryRelease、isHeldExclusively;

是FairSync、NonFairSync的父类,定义了通用的tryRelease、isHeldExclusively方法

FairSync

描述:

公平的同步对象,需要初始化ReentrantLock时传入true:new ReentrantLock(true)。

NonFairSync

描述:

非公平的同步对象,初始化ReentrantLock时默认为非公平

核心方法

  • tryAcquire
  • nonfaireTryAcquire
  • tryRelease

tryAcquire(公平锁获取资源方式)

/*
*将资源设置为0或可重入时返回true,否则返回false
*/
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
  // 判断当前状态是否为0,为0代表有资源,可以抢占
    if (c == 0) {
        // 判断前面是否还有等待节点,false证明没有,当前线程所在的节点就是第一个
        if (!hasQueuedPredecessors() &&
            // CAS设置状态,期望0,目标1
            compareAndSetState(0, acquires)) {
            // 将当前线程设为拥有资源的线程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 不为0则判断是否可以重入:判断持有资源的线程是否与当前线程一致,一致的话+1,并且不需要再CAS设置状态
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

nonfaireTryAcquire(非公平锁获取资源方式)


/**
* 将资源设置为0或可重入时返回true,否则返回false;
* 与公平锁tryAcquire唯一不同是,当state为0时,没有hasQueuedPredecessors判断
*/
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
      // 这里直接进行CAS获取资源,没有判断是否有前一个节点
        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;
}

tryRelease(公平/非公平锁通用释放逻辑)

// 如果state为0,则释放成功
protected final boolean tryRelease(int releases) {
   // 计算最新state =  AQS.state - 1
    int c = getState() - releases;
    // 当前线程必须与拥有资源的线程是同一个
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // 如果最新state为0,说明资源可以成功被释放了,并且将拥有资源的线程设为null
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
   // 最新state不为0,说明当前资源已被重入,无需释放资源 
    setState(c);
    return free;
}

问题点

  1. 公平锁与非公平锁区别

ReentrantLock初始化:true代表公平、false:非公平,无参默认非公平;

lock: 非公平锁会直接先进行CAS设置state,失败后再调用AQS#acquire,公平锁直接调用acqurie(jdk11版本两者一致);

tryAcquire:公平锁会调用AQS#hasQueuedPredecessors,是第一个节点后再CAS, 非公平锁没有该判断;

# 公平锁lock
final void lock() {
    acquire(1);
}

# 非公平锁lock
final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

公平性通常会降低吞吐量,但会降低可变性并避免饥饿。