ReentrantLock可重入锁源码剖析

目录

成员变量

构造方法

同步器实现 Sync/NonfairSync/FairSync

NonfairSync中的加锁

FairSync加锁

解锁unlock


有了AQS的基础后(AQS(AbstractQueuedSynchronizer)源码初识),可以进一步分析基于AQS独占模式实现的Reentrantlock。

首先来看看成员变量

成员变量

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** 提供所有实现机制的同步器 */
    private final Sync sync;
  
    /**
     此锁的同步控制基础。 下面细分为公平和非公平版本。 使用 AQS 状态来表示锁的持有次数。
     */
    abstract static class Sync extends AbstractQueuedSynchronizer{...}
  
    /**
     非公平锁的同步对象
     */
    static final class NonfairSync extends Sync {...}
  
  	
    /**
     *公平锁的同步对象
     */
    static final class FairSync extends Sync {...}
  
  
  ...

} 	

        可重入的互斥锁ReentrantLock与synchronized方法和语句访问的隐式监视器锁具有相同的基本行为和语义,但具有扩展功能。

        ReentrantLock由上次成功锁定但尚未解锁的线程拥有。 当另一个线程不拥有锁时,调用lock的线程将返回并成功获取锁。 如果当前线程已经拥有锁,该方法将立即返回(即可重入的)。 这可以使用方法isHeldByCurrentThread和getHoldCount进行检查。

        此类的构造函数接受一个可选的公平参数。 当设置为true ,在争用true下,锁倾向于授予对等待时间最长的线程的访问权限。 否则这个锁不能保证任何特定的访问顺序。 使用由多个线程访问的公平锁的程序可能会显示出比使用默认设置的程序更低的总体吞吐量(即,更慢;通常慢得多),但在获取锁和保证不出现饥饿的时间上有较小的差异。 但是请注意,锁的公平性并不能保证线程调度的公平性。 因此,使用公平锁的许多线程之一可能会连续多次获得它,而其他活动线程没有进行并且当前没有持有该锁。 另请注意,未计时的tryLock()方法不遵守公平设置。 即使其他线程正在等待,如果锁可用,它也会成功。

构造方法

    /**
     创建ReentrantLock一个实例。 这相当于使用ReentrantLock(false) 。
     */
    public ReentrantLock() {
        sync = new NonfairSync(); //默认非公平锁
    }

    /**
		使用给定的公平策略创建ReentrantLock的实例。
		参数:
		公平 - 如果此锁应使用公平排序策略,则为true
     */
    public ReentrantLock(boolean fair) { //指定是否公平
        sync = fair ? new FairSync() : new NonfairSync();
    }

 默认非公平锁,可传入true实现公平锁。

同步器实现 Sync/NonfairSync/FairSync

首先是NonfairSync和FairSync的抽象基类Sync

    /**
		此锁的同步控制基础。 下面细分为公平和非公平版本。 使用 AQS 状态来表示锁的持有次数。
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         抽象lock:根据其为公平锁或非公平锁决定其实现
         */
        abstract void lock();

        /**
         执行不公平的 tryLock。 tryAcquire 在子类中实现,但两者都需要对 trylock 方法进行非公平尝试:
         非公平锁需要使用,提前在这里写出
         */
        final boolean nonfairTryAcquire(int acquires) {
            ....
              //NonfairSync中说明
        }

        protected final boolean tryRelease(int releases) { // 通用release
            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;
        }

        /**
         * 从流中重构实例(即反序列化它)。
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

NonfairSync中的加锁

/**
 * 非公平锁的同步器
 */
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     执行锁定。 尝试立即驳船,在失败时备份到正常获取。
     */
    final void lock() {
        if (compareAndSetState(0, 1)) //获取锁成功
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1); //再次尝试获取,这就调到了AQS的acuire中
    }

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


public final void acquire(int arg) {
        if (!tryAcquire(arg) && //尝试获取锁,如果成功就直接返回,这里重写了成为nonfairTryAcquire
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//addWaiter:标记为独占模式进入队列 acquireQueued:线程阻塞在等待队列中获取锁,一直获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
            selfInterrupt(); //拿不到锁,自我中断
    }

 final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread(); //当前线程
            int c = getState();//当前线程状态
            if (c == 0) { //新节点入队的默认状态
                if (compareAndSetState(0, acquires)) {//cas设置state,如果成功
                    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的思想尝试addWaiter创建新节点装载该线程任务放入尾部。然后acquireQueued自旋查看自己是否为头结点。中途失败的话会需要将找到第一个可用的前驱节点,将其状态改为SIGNAL。

FairSync加锁


        /**
         tryAcquire 的公平版本。 除非递归调用或没有服务员或是第一个,否则不要授予访问权限。
         */
        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;
        }

        其实就是看目前该线程是不是队列中头结点中的线程,如果是才行,即不能提前自旋。

解锁unlock

   /**
		尝试释放此锁。
		如果当前线程是此锁的持有者,则持有计数递减。 如果保持计数现在为零,则锁定被释放。 如果当前线程不是此锁的	持有者,则抛出IllegalMonitorStateException 。
		抛出:
		IllegalMonitorStateException – 如果当前线程没有持有这个锁
     */
    public void unlock() {
        sync.release(1);
    }



    /**
以独占模式发布。 如果tryRelease返回 true,则通过解除阻塞一个或多个线程tryRelease实现。 此方法可用于实现方法Lock.unlock 。
参数:
arg – 释放参数。 这个值被传递给tryRelease但不会被解释并且可以代表任何你喜欢的东西。
返回:
tryRelease返回的值
     */
    public final boolean release(int arg) {
        if (tryRelease(arg)) { //ReentrantLock重写,直接使用Sync的,FairSync和unFairSync一样
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h); //唤醒后继节点,直接用的AQS的
            return true;
        }
        return false;
    }

Sync通用的tryRelease。

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

        被唤醒的节点然后就能在自旋中获得同步状态,成为首节点。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值