知识图谱整理之Java基础ReentrantLock

ReentrantLock介绍

ReentrantLock也是在JUC并发包下的,实现可重入锁的机制。也是在AQS基础之上的,在上一篇AQS的讲解基础下,理解这篇会比较的简单。知识图谱整理之Java基础AbstractQueuedSynchronizer

ReentrantLock的概览

要了解这个类,主要还是从它的使用方法上入手,因为这个是的数据结构基础是在AQS之上的,所以内部不再重写数据结构,主要关注点还是在于这个类的方法。

在这个讲输出的年代,ReentrantLock是如何实现输出的呢?他其实是内部维护了一个继承AQS的类,然后在通过获取锁是否公平的方式分为了两种策略,即公平锁和非公平锁。首先我们来看下这个基础类Sync源码。

源码解读

Sync内部类源码

abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        abstract void lock();

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        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) // overflow
                    throw new Error("Maximum lock count exceeded");
                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);
            }
            setState(c);
            return free;
        }

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

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

        // Methods relayed from outer class

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

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

        /**
         * Reconstitutes the instance from a stream (that is, deserializes it).
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }
  • 首先可以看到这个类是个抽象类,所以可以确定它肯定有子类实现。
  • nonfairTryAcquire方法比较关键,是非公平尝试获取锁,里面的逻辑其实就比较简单了,无非是先获取state变量的值,通过CAS方式修改,如果修改成功就代表获取到了锁标识。如果失败,会判断当前线程是否为已获取到锁标识的线程,如果是则state+1来代表重入次数。
  • tryRelease的逻辑就不多说了,就是关于state变量操作
  • 这个类主要是这两个方法比较重要,其他的等我们用到了在做深入

NonfairSync内部类源码

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

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
  • 这里就实现了Sync的lock方法,也比较的简单
  • 如果获取锁不成功,我们看到调用了acquire,这是AQS的方法,还记得这里的操作么?不记得可以看看AQS的,大概就是调用了tryAcquire方法,如果不行就放进同步等待队列。
  • tryAcquire就是调用了Sync的nonfairTryAcquire方法,这样整体就串起来了

FairSync内部类源码

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

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

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        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方法,很简单就是调用了AQS的acquire,在调用FairSync实现的tryAcquire方法
  • tryAcquire是公平锁的关键,就是在state为0时,即可以争取锁时,哪些线程可以争。
  • hasQueuedPredecessors方法很关键,这个方法作用是查询是否有任何线程在等待获取比当前线程更长的。
hasQueuedPredecessors方法源码
public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        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());
    }
  • 第一个条件 h != t代表如果当前没有任何其他竞争线程在队列中的,直接返回false。h==t代表没有其他在等待的竞争线程
  • ((s = h.next) == null || s.thread != Thread.currentThread())这个条件判断是否头结点的下一个节点的线程是否是当前线程。这里要结合这个方法的上层,是判断如果非则继续执行,所以整体逻辑就是如果是当前线程,则继续往下执行。

锁如何对外输出

从构造方法开始:

public ReentrantLock() {
        sync = new NonfairSync();
    }
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

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

其实就是用了策略模式,确定锁策略,实现交给对应策略子类。

今日总结

今天阅读了关于ReentrantLock的源码,在理解了AQS的基础上,阅读起来比较简单,再来晒下今天的知识图谱输出:知识图谱

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值