Java并发编程之重入锁ReentrantLock源码解析

概述

什么是重入锁

重入锁ReentrantLock是显示锁的一种,是Lock接口最常见的实现,采取独占式资源获取方式,即一条线程持有资源锁时,其他线程必须阻塞直到线程释放锁,重入锁的特点是同一个线程可以多次获取锁。

重入锁基于队列同步器AQS实现,同时支持Condition多条件队列(等待队列),在实现了AQS的基础上,提供了公平和非公平两种锁的获取方式。

ReentrantLock源码解析

成员变量与构造方法

首先看一下ReentrantLock除了serialVersionUID以外唯一的成员变量:

private final Sync sync;

该成员变量是提供所有实现机制的同步器,下文会详析Sync类,围绕该变量,衍生了下列构造方法:

	public ReentrantLock() {
        sync = new NonfairSync();
    }
    /** FairSync类和NonfairSync类分别对应公平锁和非公平锁的实现 */
	public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

从构造方法可见,重入锁内默认实现的是非公平锁,可以通过传入参数改实现为公平锁

公平锁与非公平锁

此处简述公平锁与非公平锁的区别与特点:

  • 非公平锁:当多个线程需要获取锁的时候,线程直接尝试获取,不会管队列中其他线程等待了多久,即无序获取,这样可能引发被称为饥饿的情况,即等待获取资源很久的线程仍不能获取锁。
  • 公平锁:当线程需要获取锁时,直接加入队列队尾,每当有锁被释放,总是队列中等待时间最长的线程获取锁,即队首获取锁,保证了有序获取,这样能够减少饥饿情况发生的概率。

虽然公平锁可以有效减少饥饿的发生,但公平锁的效率往往没有非公平锁高,非特殊需求应采用非公平锁以保证效率,也许正因如此重入锁的无参构造默认实例化的是非公平锁的实现。

锁的实现

AQS为各类同步工具提供了基本框架,各同步工具主要负责实现tryAcquire等方法以及对资源状态state的实际逻辑实现,下文中诸如acquireQueued的方法以及资源状态state的详解还请移步专栏内AQS文章。

静态内部类Sync

Sync直接继承队列同步器AQS,内部主要实现了非公平获取锁和释放锁的方法,解析见注释部分,非重要代码省略,源码如下:

	abstract static class Sync extends AbstractQueuedSynchronizer {

        abstract void lock();
		
		/** 此方法就是非公平尝试获取锁的具体实现 */
		/** 参数acquires为线程想要获取的资源(锁)数量 */
        final boolean nonfairTryAcquire(int acquires) {
        	/** 首先获取当前线程 */
            final Thread current = Thread.currentThread();
            /** 获取当前状态 */
            int c = getState();
			/** 为0时表示当前没有线程持有锁 */
            if (c == 0) {
            	/** CAS原子修改state的值 */
                if (compareAndSetState(0, acquires)) {
                	/** 设置当前线程拥有独占访问权限 */
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            /** 判断当前线程是否持有锁 */
            else if (current == getExclusiveOwnerThread()) {
            	/**
            	* 如果是当前线程持有锁
            	* 就将state原值加上acquires作为新值 表示当前线程持有锁的数量
            	*/
                int nextc = c + acquires;
                if (nextc < 0) 
                    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() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
		/** 创建一个新的Condition条件队列 */
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
    }

非公平锁NonfairSync

	static final class NonfairSync extends Sync {

        /** 重写Lock的lock方法,使用时获取锁实际调用的就是该方法 */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
		/** 此处直接调用Sync中实现的获取资源方式即可 不需要重写 */
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

AQS中的acquire方法如下

	public final void acquire(int arg) {
		/** 执行一次获取锁的尝试 在非公平实现下也就是调用Sync的nonfairTryAcquire() */
        if (!tryAcquire(arg) &&
        	/**
			* 实际获取锁的操作都在acquireQueued方法内实现
			* addWaiter方法用来向同步队列中添加封装了当前线程的节点
			* 均非本文讨论重点 不展开叙述
			*/
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

公平锁FairSync

	static final class FairSync extends Sync {

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

		/** 公平版本的锁获取尝试 覆盖重写Sync中的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;
        }
    }

hasQueuedPredecessors()方法的作用是判断是否有线程等待时间比当前线程更长,该方法是公平锁与非公平锁实现差异的体现,源码如下:

	public final boolean hasQueuedPredecessors() {
        Node t = tail; 
        Node h = head;
        Node s;
        /**
        * 先看第一个判断 如果h==t时 说明等待队列中一定只有一个节点
        * 即只有正在尝试获取锁的节点 直接返回false
        * 否则进入第二个判断 
        * 只有当同步队列的头节点有后继节点 且该后继节点封装的线程是当前线程时
        * 返回false 因为同步队列的第二个节点一定是同步队列中等待时间最长且未持有锁的线程
        * 其余情况均返回true 即当前线程前有比该线程等待时间更久的线程
        */
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

以上便是本篇文章的全部内容,建议搭配AQS和Condition文章食用
作者才疏学浅,如文中出现纰漏,还望指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

7rulyL1ar

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值