【JUC】ReenTrantLock公平锁,非公平锁源码分析

其中有一些值的概念不太清楚,参考了:
https://blog.csdn.net/lsgqjh/article/details/63685058(这一位大佬,讲的很细!!)
https://blog.csdn.net/mulinsen77/article/details/84583716
在此感谢!

Lock接口功能:

public interface Lock {
    // 获得锁
	void lock();
    // 获得锁
    void unlock();
    // lock非阻塞版本,成功返回true
    boolean tryLock();
    // 添加尝试时间,时间到返回false
    boolean tryLock(long time, TimeUnit unit)
    // 返回一个监视器对象
    Condition newCondition();
}

AQS

在看ReenTrantLock之前,先大致看看AQS都做了什么:

这是一个简化的线程队列模型:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t4Mu45st-1586024262498)(image/aqs.png)]

重要属性:

  • state:代表了资源是否处于锁定状态;

    1:锁定(已经有线程拿锁,如果重入了,此值一直累加)2:未锁定

    线程拿锁,就是通过CAS修改state,修改成功,则拿到锁;

  • Node内部类:

    每一个Node装载一个线程;对线程通过双向链表的方式排队;

  • Node内部类:还定义资源是 独占 / 还是共享
    也就是每个线程都有一个mode,标识是独占,还是共享;

    Node EXCLUSIVE:代表独占;

    Node SHARED:代表共享;

先看几个重要方法,后面会用到;

acquire

acquire:顾名思义获取,获取锁的方法

tryAcquire:就先当作是获得锁,返回true,没拿到锁,返回false;这个方法是ReenTrantLock的方法,后面会讲;

addWaiter:如果没拿到锁,将当前要拿锁的线程加入线程队列

acquireQueued:已经入队完成,会进一步判断,后面讲;主要是判断线程是否被挂起;

  • true:挂起
  • false:拿锁成功了

所以:

当:拿锁失败,并且线程被挂起,就会执行selfInterrupt();,执行线程的中断;

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

addWaiter

这个方法就是:(CAS操作入队)

让当前线程包装成Node,

队列存在,直接入队

如果队列不存在,调用enq(node);,初始化队列,再入队

(入队:将node设置为队列的tail尾部节点,并且与tail双向关联起来)

private Node addWaiter(Node mode) {
    // 包装线程为Node,并且是独占的
    Node node = new Node(Thread.currentThread(), mode);
    // 拿到线程队列的尾节点
    Node pred = tail;
    // 如果pred存在,即队列非空
    if (pred != null) {
        node.prev = pred;
        // CAS操作成功入队,将Node设置为tail
        if (compareAndSetTail(pred, node)) {
            // 因为是双向链表,要再链一次
            pred.next = node;
            return node;
        }
    }
    // 队列为空,调用enq,初始化队列,并入队
    enq(node);
    return node;
}

acquireQueued

此方法:是线程没拿到锁,入队之后执行;

主要是:如果发现当前线程的前一个结点是队列的head,那么会再次尝试拿锁;

也就是说,此时线程队列,就俩线程,一个是head,持锁线程,一个是当前线程;

那么当前线程会不断尝试拿锁(for (;;)

最终返回的boolean型interrupted

  • true:挂起
  • false:拿锁成功了
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            // 拿到当前线程的前一任节点
            final Node p = node.predecessor();
            // 发现前任是head,再次尝试拿锁
            if (p == head && tryAcquire(arg)) {
                // 拿锁成功,node设置为head
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // 判断是否将当前线程挂起
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

ReenTrantLock

实现Lock接口

ReenTrantLock只有一个内部属性:就是Sync内部类的锁抽象对象

// 这是一个父类,两个子类分别实现公平锁,非公平锁
private final Sync sync;

三个内部类:

  • Sync(继承AQS):锁抽象;
  • NonfairSync(继承Sync):非公平锁抽象;
  • FairSync(继承Sync):公平锁抽象;

构造器

我们在创建ReenTrantLock对象,调用构造器时,就会创建不同的Sync(锁抽象的实现)

// 非公平锁
public ReentrantLock() {
    sync = new NonfairSync();
}
// 公平锁
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

公平锁源码(拿锁,排队,重入锁)

当我们调用了lock.lock();

公平锁下,sync已经是FairSync的实例了;

调用sync.lock()

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

然后调用FairSync内部类下的lock方法:(建议点进源码,看下)

acquire(1):此方法是AQS下的方法;(去上面看!)在内部是调用了下面的tryAcquire方法;

这个参数1是干嘛的:就代表尝试获取锁;之前AQS的属性state,如果为0表示未锁定;

这个1就是要通过compareAndSetState(0, acquires)CAS操作进行加锁的;

(这里也是通过内存地址stateOffset,拿到state的状态,CAS操作不再赘述)

尝试将state设置为1,即拿到锁;

重点:tryAcquire方法(实现了拿锁,排队,重入锁)

static final class FairSync extends Sync {
    final void lock() {
        acquire(1); // 调用AQS acquire方法,前面讲了
    }
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        // 从AQS中拿到当前资源的state状态
        int c = getState();
        // 如果为0,则表示未锁定,可以尝试获取锁
        if (c == 0) {
            // hasQueuedPredecessors是看当前线程队列中是否有其他线程(非公平锁没有此判断)
            // 如果有其他线程,当前线程不允许拿锁,而是去排队
            // 如果没有线程,并且CAS操作将state置1,那么当前线程就拿到了锁
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                // 设置独占的资源持有者为当前线程,即拿锁,并返回true
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // state非0,即资源已被锁定
        // 判断当前的线程,是不是占用锁的线程
        // 是,则累加state,也就是重入锁的实现
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            // 叠加state状态
            setState(nextc);
            return true;
        }
        return false;
    }
}

非公平锁源码

同样是lock()方法,不再赘述,只不过这里的Sync实例,是NonfairSync的实例;

与公平锁的主要区别:线程是否排队

直接看NonfairSync

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;
	// 加锁方法
    final void lock() {
        // CAS尝试加锁
        if (compareAndSetState(0, 1))
            // 成功,设置资源独占者为当前线程
            setExclusiveOwnerThread(Thread.currentThread());
        else
            // 底层依然调用下面的tryAcquire
            acquire(1);
    }

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

nonfairTryAcquire方法是其父类Sync下的方法

类似于公平锁的tryAcquire方法

区别是:不再进行hasQueuedPredecessors()方法的判断,直接尝试获取锁

final boolean nonfairTryAcquire(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;
        if (nextc < 0) 
            throw new Error("Maximum lock count exceeded");
        // 叠加state状态
        setState(nextc);
        return true;
    }
    return false;
}

释放锁

释放锁的过程:

并不是说直接将state置为0即可,因为可能发生了多次重入;

每调用一次tryRelease,state减一;

protected final boolean tryRelease(int releases) {
    // 当前state-1
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // state如果为0,可以释放锁
    if (c == 0) {
        free = true;
        // 独占线程设为null
        setExclusiveOwnerThread(null);
    }
    // 不能释放锁,state设置为c,即:减一操作;
    setState(c);
    return free;
}
posted @ 2019-10-11 09:13  mussessein  阅读(223)  评论(0编辑  收藏  举报