2.2 可重入锁ReentrantLock

可重入互斥锁与使用synchronized方法和语义访问的隐式监视器锁具有相同的基本行为和语义,但具有扩展功能。

ReentrantLock由上次成功锁定但是尚未解锁的线程拥有。当另一个线程不拥有锁时,调用lock的线程将返回并成功获取锁

ReentrantLock实现了公平锁和非公平锁,由构造函数的入参控制:

  • 当入参为true时,为公平锁,锁倾向于对等待时长最长的线程授权使用公平锁。多个线程访问公平锁一般而言会比非公平锁慢,但是在获取锁和不出现饥饿的时间差异较小。锁的公平性并不能保证线程调度的公平性。因此所用公平锁的许多线程之一可能连续多次获取锁。另外,使用未计时的tryLock方法不遵守公平设置。即使其他线程在等待,如果锁可用,他也会成功

该锁最多支持同一个线程2147483647个递归锁。该类的序列化与内置锁的行为方式相同:反序列化的锁处于解锁状态,不管器序列化时的状态如何

具体实现

其内部对AQS做了两层实现:

  • 第一层为通过Sync抽象类继承AQS,实现基础的获取与释放
  • 第二层为定义公平锁和非公平锁具体实现lock等方法

Sync

Sync抽象类实现了一些基础方法,以及非公平锁的获取。使用AQS状态来表示锁的持有次数。
Sync实现的基础方法有:tryReleaseisHeldExclusivelytryRelease:

//返回当前同步器是否空闲。入参release为1
protected final boolean tryRelease(int releases) {
    //使用state记录同步器的持有次数,每次释放state-1
    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;
}

isHeldExclusively:

protected final boolean isHeldExclusively() {
    // While we must in general read state before owner,
    // we don't need to do so to check if current thread is owner
    return getExclusiveOwnerThread() == Thread.currentThread();
}

问题:

  1. tryRelease为什么要判断当前同步器的持有者是否为当前线程
  2. 如果判断为什么不用!isHeldExclusively方法

Sync定义了lock接口。实现了非公平锁的获取方法。nonfairTryAcquire:

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    //获取当前同步器状态,主要还是看当前同步器是否被占用
    int c = getState();
    
    if (c == 0) {
    //如果没有被占用,直接尝试获取锁。如果此时获取失败了,那么当前方法失败
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //如果被占用了,判断占用线程是否为当前线程。如果是,那么设置同步器状态+1;如果不是,那么当方法失败
    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;
}

可以看到重入锁的实现就是对判断持有锁的线程是否为当前线程,如果是,修改锁的状态。

Sysnc没有实现AQS定义的tryAcquire方法。而ReentrantLock是独占锁,所以tryAcquireSharedtryReleaseShared方法也不需要实现。那么tryAcquire就需要Sync的两个子类NonfairSyncFairSync来实现。

NonfairSync

先明确非公平锁的定义:每个线程获取锁的顺序是随机的,并不遵循先来先得的规则,任何线程在某时刻都有可能直接获取到锁。

那么其tryAcquire的实现必然就是SyncnonfairTryAcquire方法的实现。然而nonfairTryAcquire可能获取失败,从锁的获取来讲,获取不到的话,应该阻塞当前线程,直到再次有锁释放,参与锁的竞争。那么其应该实现了Synclock方法:

final void lock() {
    //首先尝试获取锁
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
    //获取不到则调用acquire方法,走一般的同步器获取流程
        acquire(1);
}

在竞争不激烈的情况下,非公平锁抢锁容易成功;如果在竞争激烈的情况下,acquire将当前线程加入同步队列,那么非公平锁的效率和公平锁的效率应该是一样的。

FairSync

公平锁的定义:多个线程按照申请锁的顺序获取锁。
tryAcquire:

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;
}

公平锁在抢锁时,通过判断是否存在前继节点保证先申请锁的线程先获取锁。那么为了保证前继节点获取到锁,其lock方法,相对NoFairSynclock方法应该直接调用acquire方法,避免当前线程直接抢到锁。lock实现:

final void lock() {
    acquire(1);
}

ReentrantLock在实例化时通过入参fair来确定使用FairSync还是NonfairSync,默认为NonfairSyncReentrantLock实现的Lock的几个接口都是对同步器的调用。其中需要注意的是tryLock()接口。

tryLock接口的语义为仅在空闲时获取锁。如果锁可用,则立即获取锁并返回true;如果锁不可用则方法立即返回false。所以tryLock接口应该直接去判断锁的状态,然后获取锁。其逻辑和非公平锁的tryAcquire应该是一致的,即调用SyncnonfairTryAcquire

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值