• ReentrantLock源码


    概述

      AQS框架下的锁都是实现Lock接口并实现tryAcquire方法,在tryAcquire方法中对state变量进行修改来改变锁的状态。

    • 重入性。获得锁的线程可以再次获得锁。
    • 公平锁。严格保证先尝试获得锁的线程能够先获得锁。
    • 非公平锁。不能严格保证先尝试获得锁的线程先获得锁。

    获得锁

    非公平锁

      cas修改state的状态,如果成功设置当前重入锁的拥有者是当前线程。cas对当前state的期待值为0,所以一个线程如果是重入自己持有的锁,在这一步是无法成功的。

      final void lock() {
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    acquire(1);
            }

      如果cas失败会调用acquire(1),即调用tryAcquire(1),最终调用的是nonfairAcquire(1),该方法是非公平锁的核心实现。

    • 如果锁的state=0,cas修改,如果成功代表获得锁成功返回true;失败返回false,进入AQS的队列。
    • 如果state!=0但当前锁的线程拥有者是当前线程,进入锁的重入阶段,修改锁的state,记录所的重入次数,返回true代表线程成功获得锁。

      非公平重入锁的tryAcquire实现相对简单

    • 在锁的内部记录锁的持有线程,方便锁的重入
    • 用state的值记录锁的重入次数
     protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
     /**
             * 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;
            }

    公平锁

      公平锁的lock方法中直接调用了acquire,而非公平锁lock方法中先cas,cas失败后才调用的acquire。这是公平性和非公平性的体现,acquire失败后会进入队列,AQS队列中的线程只有pre是head才可以尝试获得锁,即aqs通过队列的方式维护了公平性,如果把所有锁的获得都交给aqs去管理,那么锁就是公平的。相应的如果在加入aqs之前先自己尝试获取锁,那么这个锁就未必是公平的。例如:A线程获取锁的时候发现state不是0,失败进入AQS队列,B线程尝试获取锁的时候state恰好变成0,由于A线程在AQS队列尾部,A线程不具有争取锁的资格,所以B线程获得锁A没有获得锁,这就是非公平性的体现。  

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

      这里同样分为两个部分

    • state==0代表锁空闲,但是要先判断AQS里是否有前继节点,如果有代表已经有人在排队,返回false进入AQS排队
    • 如果锁的持有着是当前线程,则重入并修改state
     /**
             * 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;
            }

    课代表划重点非公平锁和公平锁

    • 实现

    1. 非公平锁在lock方法中会先进行一次cas,cas成功当前线程就获取到锁,失败后才调用tryAcquire。公平锁没有先调用cas,而是调用acquire即调用tryAcquire。
    2. 在tryAcquire中,非公平锁只要当前state=0就可以进行cas,公平锁在state=0的时候还要先判断队列里是否有等待的线程,只有队列中没有等待的线程的情况下才会cas。
    3. 无论是公平锁还是非公平锁,才tryAcquire失败进入AQS队列后,AQS的先入先出的特性能够保证后续的公平性。
    • 比较

    1. 非公平锁容易出现饥饿。一个刚刚释放锁的线程再次获取锁的概率很大,所以会出现一个线程连续多次获得锁,造成饥饿。
    2. 公平锁效率低。公平性需要线程切换从而有许多没有必要的切换。

      

    释放锁

      公平锁和非公平锁的释放没有区别,都是静态内部类sync中的tryRelease方法。

    • 如果当前线程不是锁的持有线程,抛出异常。
    • 由于释放锁的线程一定是持有锁的线程,所以释放锁修改state不需要cas,直接相减。
    • 如果释放完毕后state=0,返回true,并设置当前所的持有者为null。
    • 如果释放完毕后state=0,返回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;
            }

      

    其余获得锁的方法

      这些获得锁的方法都是公平锁和非公平锁没有区别。

    lockInterruptibly

      在获得锁失败被park时候能够相应中断,实现比较简单,直接调用AQS里的方法。

    tryLock

      非阻塞获取锁,类似NIO。如果lock没有被别的线程持有,那么tryLock会立即返回true,这种行为其实是一种非公平的锁,另外从它调用的方法nonfairTryAcquire也可以看出这一点。这也就意味着如果你申请了一个公平锁,调用一个公平锁的tryLock它的行为也是非公平的。如果lock此时被别的线程持有,tryLock会返回false。

        public boolean tryLock() {
            return sync.nonfairTryAcquire(1);
        }
            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;
            }
  • 相关阅读:
    ruby 二进制转十进制 Integer("0b101") = 5
    开始菜单和我的文档的我的图片及我的音乐变成 my pictrues 正常图标了
    ruby watir 莫名其妙的错误
    Excel SaveAS是去掉提示框
    apache && jboss安装
    ruby require include的区别
    ruby控制鼠标
    This error is raised because the column 'type' is reserved for storing the class in case of inheritance
    用正则表达式限制文本框只能输入数字,小数点,英文字母,汉字等各类代码
    ASP.NET 如何动态修改 Header 属性如添加 Meta 标签 keywords description!
  • 原文地址:https://www.cnblogs.com/AshOfTime/p/10890108.html
Copyright © 2020-2023  润新知