浅论ReentrantLock的实现

时间记录:2019-7-6
在之前的aqs的总结中有看到ReentrantLock的身影,故去看了下ReentrantLock的源码,我在这里进行一下的总结自己整理的内容。
以下【】的定义内容,均是从jdk1.8版本的源码翻译过来的,翻译内容不一定准确,结合自己的理解总结下。
1:ReentrantLock的定义

A reentrant mutual exclusion {@link Lock} with the same basic behavior and semantics as the implicit monitor lock accessed using {@code synchronized} methods and statements, but with extended capabilities.
【可重入互斥锁具有与使用synchronized方法和语句访问的隐式监视器锁相同的基本行为和语义,但具有扩展功能】,这里基本上说明这个锁的是可重入与互斥的锁。
重入: 重入就是多次获取这个锁,但是是在一个线程中
互斥: 互斥就是只能一个线程获取这个锁,必须经过lockunlock的过程,如果其余线程想要获取到这个锁,会在等待这个锁

A {@code ReentrantLock} is owned by the thread last successfully locking, but not yet unlocking it. A thread invoking {@code lock} will return, successfully acquiring the lock, when the lock is not owned by another thread. The method will return immediately if the current thread already owns the lock. This can be checked using methods {@link #isHeldByCurrentThread}, and {@link#getHoldCount}.
【锁由最后成功锁定的线程拥有,但尚未解锁。当锁不是由另一个线程拥有时,调用lock的线程成功获取锁将返回。如果当前线程已拥有锁,则该方法将立即返回。可以使用isHeldByCurrentThread和getHoldCount方法检查】,这里是指当一个线程获取到了这个锁的时候,另一个线程没有获取到这个锁,那么在调用lock的时候,已经获取到这个锁的线程会立即返回,而没有获取到这个所得线程会在获取到锁的时候返回。

The constructor for this class accepts an optional fairness parameter. When set {@code true}, under contention, locks favor granting access to the longest-waiting thread. Otherwise this lock does not guarantee any particular access order. Programs using fair locks accessed by many threads may display lower overall throughput (i.e., are slower; often much slower) than those using the default setting, but have smaller variances in times to obtain locks and guarantee lack of starvation. Note however, that fairness of locks does not guarantee fairness of thread scheduling. Thus, one of many threads using a fair lock may obtain it multiple times in succession while other active threads are not progressing and not currently holding the lock.Also note that the untimed {@link #tryLock()} method does not honor the fairness setting. It will succeed if the lock is available even if other threads are waiting.
【此类的构造函数接受可选的fairness(boolean型)参数,当参数设置为true时,在锁的争用情况下,锁有利于对等待时间最长的线程的访问权限授予。否则,此锁将不保证任何特定的访问顺序。使用由许多线程访问的公平锁的程序可以显示比使用默认设置的程序更低的总吞吐量(即更慢;通常慢得多),但是获得锁的时间变化较小并且保证不饥饿(这里应该指的是线程饥饿状态)。但请注意,锁的公平性并不能保证线程调度的公平性。因此,使用Fair锁的众多线程之一可以连续多次获得,而其他活动线程没有进展,当前没有持有同时注意,不定时的tryLock方法没有尊重公平背景。如果锁即使可用,即使其他线程正在等待,它也会成功。】,这里主要说的是锁的公平的和不公平的,主要只在获取锁时的争夺问题,公平的即等待的线程获取锁时时按照顺序来执行的而不公平的话等待的线程获取锁时不确定,不知道是等待的线程中的哪一个。

It’s a good idea to always follow the {@code lock} call immediately with the {@code try} block, most often before/after construction, for example:【最好始终使用 try块跟随lock调用,最常见的是在构造之前/之后,例如:】

class X {
   private final ReentrantLock lock = new ReentrantLock();
   public void m() {
      lock.lock();  // block until condition holds
      try {
         // ... method body
      } finally {
         lock.unlock()
      }
   }
}

整体上来说可重入互斥锁,可以简单的理解为在不同线程时互斥的,而在同一线程时重入的

2:查看构造函数
构造函数从上面的介绍中可以看出来分为公平性和非公平性两种

    /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

从构造函数上来看我们可以发现,包含了FairSync和NonfairSync,我们来看看下这个公平和非公平的不同实现,以及如何实现可重入,至于互斥不仔细描述

3:FairSync 和 NonfairSync
从FairSync和NonfairSync来看我们发现同一继承了Sync ,而Sync 继承了aqs,ReentrantLock的可重入互斥主要就是依据这几个内容实现

    /**
     * Sync object for fair locks
     */
    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;
        }
    }

FairSync : 从方法来看主要是包含了tryAcquire和lock方法,那么这两个方法在ReentrantLock的lock和unlock中充当着什么角色呢。
我们可以发现它是从ReentrantLock.lock()->FairSync.lock()->FairSync.tryAcquire()的一个顺序,那么我们在FairSync.lock()可以看到一个acquire(1)[aqs中的方法]的一个方法那么这个方法是怎么到FairSync.tryAcquire()的呢 以及他具体干了什么事呢

    /**
     * Acquires in exclusive mode, ignoring interrupts.  Implemented
     * by invoking at least once {@link #tryAcquire},
     * returning on success.  Otherwise the thread is queued, possibly
     * repeatedly blocking and unblocking, invoking {@link
     * #tryAcquire} until success.  This method can be used
     * to implement method {@link Lock#lock}.
     *
     * @param arg the acquire argument.  This value is conveyed to
     *        {@link #tryAcquire} but is otherwise uninterpreted and
     *        can represent anything you like.
     */
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

我们发现其执行了tryAcquire(1),那么也就是FairSync.tryAcquire(1),我们来看这里的流程怎么走的,首先会判断一个state(其实就是一个记数,可以参考之前的AQS中的总结)的值,我们会发现state的值为0后会执行hasQueuedPredecessors的操作,主要是为了查询是否存在等待时间比当前线程等待时间更长的线程,而通过hasQueuedPredecessors方法可以看出是通过队列中的线程顺序进行判断而不是真的多长时间。之后会执行setExclusiveOwnerThread也就是将当前线程设置为这个锁的独占线程。如果c不等于0呢?那么下面就是体现可重入的地方了,我们发现会将当前线程和独占线程exclusiveOwnerThread进行对比,如果是在同一个线程那么会将state的值加一,从这个方法我们就可以看出了为什么可以重入,实际上所谓的重入还是在原有的state上面加1,保证其可以执行被锁的部分【可以同时对比countdownlatch的state的-1操作】。从AQS.acquire可以看出来,如果当前线程获取到了这个锁,那么就会结束操作,如果没有获取到锁会将当前线程以Node的方式保存在队列中,然后将当前线程Interrupt(中断),那么这个被中断的线程如何重新启动,那么对应的就是释放,对应的就是unlock,对应的是aqs.unparkSuccessor(),其按照队列的顺序进行清除线程中断状态。

我们看到了公平是按照队列的顺序进行的,那么非公平的是怎么给等待的线程安排锁的呢

    /**
     * Sync object for non-fair locks
     */
    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);
        }
    }

我们看到tryAcquire(1)中调用的方法是nonfairTryAcquire(1),我们对比发现其没有查找对应的等待队列的线程的顺序,那么这就决定如果能获取到所谁到谁就可以获取
注意:这里在获取锁的开始时会调用compareAndSetState(0, 1),这里0表示预期值,1表示跟新的值,也就是预期state的值为0的时候就将当前线程给定为独占锁,也就是立即抢夺【Performs lock. Try immediate barge, backing up to normal acquire on failure.这里是这样说的,个人觉得有点牵强,而compareAndSetState实际保证了state的操作的原子性】,不为0表示已经被占了,那就要保存在队列中。

    /**
     * Base of synchronization control for this lock. Subclassed
     * into fair and nonfair versions below. Uses AQS state to
     * represent the number of holds on the lock.
     */
    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;
        }

注意:可以结合之前的AQS一起看
时间记录:2019-7-17

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园的建设目标是通过数据整合、全面共享,实现校园内教学、科研、管理、服务流程的数字化、信息化、智能化和多媒体化,以提高资源利用率和管理效率,确保校园安全。 智慧校园的建设思路包括构建统一支撑平台、建立完善管理体系、大数据辅助决策和建设校园智慧环境。通过云架构的数据中心与智慧的学习、办公环境,实现日常教学活动、资源建设情况、学业水平情况的全面统计和分析,为决策提供辅助。此外,智慧校园还涵盖了多媒体教学、智慧录播、电子图书馆、VR教室等多种教学模式,以及校园网络、智慧班牌、校园广播等教务管理功能,旨在提升教学品质和管理水平。 智慧校园的详细方案设计进一步细化了教学、教务、安防和运维等多个方面的应用。例如,在智慧教学领域,通过多媒体教学、智慧录播、电子图书馆等技术,实现教学资源的共享和教学模式的创新。在智慧教务方面,校园网络、考场监控、智慧班牌等系统为校园管理提供了便捷和高效。智慧安防系统包括视频监控、一键报警、阳光厨房等,确保校园安全。智慧运维则通过综合管理平台、设备管理、能效管理和资产管理,实现校园设施的智能化管理。 智慧校园的优势和价值体现在个性化互动的智慧教学、协同高效的校园管理、无处不在的校园学习、全面感知的校园环境和轻松便捷的校园生活等方面。通过智慧校园的建设,可以促进教育资源的均衡化,提高教育质量和管理效率,同时保障校园安全和提升师生的学习体验。 总之,智慧校园解决方案通过整合现代信息技术,如云计算、大数据、物联网和人工智能,为教育行业带来了革命性的变革。它不仅提高了教育的质量和效率,还为师生创造了一个更加安全、便捷和富有智慧的学习与生活环境。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值