11-ReentrantLock

2020-02-05  本文已影响0人  鹏程1995

类介绍

类继承关系

1.png

ReentrantLock实现了Lock接口和Serializable接口。

类介绍

可重入互斥锁,此锁依赖AQS实现了最基本的互斥锁,提供了线程在占有锁的情况下可重复获得锁的功能。

此对象的实现思路很简单,提供了一个对象继承AQS,对线程的操作全部转化为直接或者间接对AQS的方法的依赖。

注意

ReentrantLock提供了两种获得锁的方法,一种是不公平的,一种是相对公平的。

公平的那个不保证一定公平。这个我们在具体介绍时会详细介绍。

遗留问题

获得/释放锁时的入参 argAQS的状态state,在这里赋予了它特别的意义:占有锁的线程重入次数

源码

内部类

Sync

类介绍

此类是ReentrantLock使用AQS的基础类,此类对AQSReentrantLock需要的、公平模式和非公平模式下都要用的方法进行了实现。

方法介绍

/**
 * 可重入互斥锁的同步器的基础类。定义了两个他的子类,一个是公平的、一个是非公平的
 */
abstract static class Sync extends AbstractQueuedSynchronizer {
    // 自定义 serialVersionUID ,方便代码变化后做序列化、反序列化兼容
    private static final long serialVersionUID = -5179523762034025860L;

    /**
     * 留给子类实现的接口,根据公平/不公平实现不同
     */
    abstract void lock();

    /**
     * 不公平模式下的 tryLock() 实现,由于在 ReentrantLock 中也直接调用了,
     * 所以算是通用的方法吧,放这里
     *
     * 入参为获得锁的入参 arg
     */
    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");
            
            // 这里和上面不同,这里只有自己在用,直接设置即可,上面使用 CAS 是
            // 因为锁被释放,有多个线程在竞争,可能有多个线程在同时修改 state 那
            // 个变量
            setState(nextc);
            return true;
        }
        return false;
    }

    // 互斥模式的释放都一样,不涉及公不公平的问题,所以就在这里实现了
    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);
        }
        // 这里将 c 设置成0,才有人竞争,所以这里还是一个线程操作,不用 CAS 
        setState(c);
        return free;
    }

    protected final boolean isHeldExclusively() {
        // 判断当前线程是否占用锁
        return getExclusiveOwnerThread() == Thread.currentThread();
    }

    final ConditionObject newCondition() {
        return new ConditionObject();
    }

    // 为外部类提供的方法

    // 不明白为什么要这么判断一下,没必要,即使锁没有被占用返回的也是 null
    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }

    // 当前的锁都是互斥锁,可能是怕被错误调用吧
    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }

    
    final boolean isLocked() {
        return getState() != 0;
    }

    /**
     * 从输入流中反序列化
     *
     * 反序列化后将锁重置为未获取状态
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
    }
}

NonfairSync

类介绍

Sync的子类,提供不公平的等待

方法介绍

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * 非公平锁,有线程来了,先竞争一下锁,如果竞争到了,更改了锁的状态,就直接运行
     *
     * 失败了就走 AQS 的正经流程:
     *    先尝试调用 tryAcquire 获得一下锁,失败就进队列阻塞,等待获得锁后被唤醒
     */
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

思路提要

注意,正常的 AQS子类中提供了两个插队的地方:

  1. lock()可以插队,有线程来了先竞争一下锁
  2. AQS.acquire()可以插队,一般都是先调用tryAcqurie()竞争一下,失败了再乖乖进队

FairSync

类介绍

Sync的子类,提供公平的等待。

方法介绍

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

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

内部方法

提供了一些方便的用于查询锁当前状态的方法,可以用来做监视器等。

这里直接把方法粘过来了,都很简单

// 获得重入次数
public int getHoldCount() {
    return sync.getHoldCount();
}

// 判断是否被当前线程获得
public boolean isHeldByCurrentThread() {
    return sync.isHeldExclusively();
}

// 判断锁是否被占用
public boolean isLocked() {
    return sync.isLocked();
}

// 判断当前同步器是否公平
public final boolean isFair() {
    return sync instanceof FairSync;
}

// 获得占用当前锁的线程
protected Thread getOwner() {
    return sync.getOwner();
}

// 获得是否有竞争当前锁的线程
public final boolean hasQueuedThreads() {
    return sync.hasQueuedThreads();
}

// 获得入参线程是否在竞争当前锁
public final boolean hasQueuedThread(Thread thread) {
    return sync.isQueued(thread);
}

// 获得竞争锁的队列长度
public final int getQueueLength() {
    return sync.getQueueLength();
}

// 获得竞争锁的所有线程
protected Collection<Thread> getQueuedThreads() {
    return sync.getQueuedThreads();
}

// 判断是否有线程被 Condition 阻塞等待唤醒
// 判断条件同步队列中是否有线程
public boolean hasWaiters(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}

// 获得条件同步队列中线程个数
public int getWaitQueueLength(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}

// 获得条件同步队列中的线程
protected Collection<Thread> getWaitingThreads(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
}

暴露的方法

构造方法

/**
 * 非公平锁
 */
public ReentrantLock() {
    sync = new NonfairSync();
}

/**
 * 可以公平锁
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

lock()

介绍

阻塞等待获得锁,依赖构造函数生成的Sync

源码

public void lock() {
    sync.lock();
}

lockInterruptibly()

介绍

阻塞等待锁,可打断,打断抛异常

源码

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

tryLock()

介绍

尝试获得锁,非阻塞,根据返回值判断是否获得锁。

直接竞争锁,不排队,在公平模式下也可以调用

源码

public boolean tryLock() {
    return sync.nonfairTryAcquire(1);
}

tryLock(long timeout, TimeUnit unit)

介绍

尝试获得锁,限时、可打断,阻塞获得

返回值表示是否在超时前获得

源码

// 返回 true 表示获得锁
// 返回 false 表示超时
public boolean tryLock(long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

unlock()

介绍

释放本线程获得的锁【释放一次,也就是重入次数-1,减到0时释放锁】

源码

public void unlock() {
    sync.release(1);
}

newCondition()

介绍

创建 Condition对象,用来使获取锁执行一半的线程放弃锁阻塞

源码

public Condition newCondition() {
    return sync.newCondition();
}

扩展

参考文献

上一篇 下一篇

猜你喜欢

热点阅读