java并发编程——ReentrantLock源码(重入锁、公平锁、非公平锁)

是什么?

可重入:指当前线程获取锁之后可以再次获取这个锁,而不会被阻塞。

:实现了Lock接口,通过实现内部AQS子类,完成锁的实现。并且ReentrantLock是排他锁

排他锁与共享锁
对于JDK并发中,所谓的排他锁主要是实现了tryAcquire的锁(ReentrantLock、ReentrantReadAndWriteLock中的writeLock)。

共享锁是实现了AQS.tryAcquireShared的锁(ReentrantReadAndWriteLock中的读锁、Semaphore、CountDownLatch)

源码解析

阅读ReentrantLock需要对AQS有一定理解,请查看AQS解析

ReentrantLock实现了Lock接口,其中通过使用AbstractQueuedSynchronizer实现了公平锁与非公平锁。

ReentrantLock内部定义了一个AQS的抽象子类Sync。然后通过继承这个抽象类,分别实现了FairSync类、NonFairSync类 用来是先公平锁、非公平锁(默认)。并在这两个子类中实现了 AQS的tryAcquire模板方法。公平与非公平锁的区别主要体现在tryAcquire的实现上:

这里写图片描述

先看ReentrantLock.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.
		 */
		// 非公平式获取锁,非公平锁barge失败后会执行。lock(非公平模式)、tryLock的实现逻辑
		final boolean nonfairTryAcquire(int acquires) {
			final Thread current = Thread.currentThread();
			int c = getState();
			if (c == 0) {// 当前锁是否空闲
				if (compareAndSetState(0, acquires)) {// CAS获取锁
					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);// 更新state状态为当前重入次数
				return true;
			}
			return false;// CAS失败 、也不属于当前线程重入的情况。获取锁失败,返回
		}

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

接着看ReentrantLock.Sync的两个子类.

ReentrantLock.NonfairSync

	/**
	 * 非公平锁(ReentrantLock默认模式): 
	 * 1.直接使用CAS去尝试获取锁(也就是同步状态state),
	 * 	成功后当前线程置为拥有排他锁的线程。返回
	 * 
	 * 2.如果失敗调用AQS-acquire(),与公平锁的区别主要体现在tryAcquire上。
	 * 	判断当前同步状态是否空闲: 
	 * 		空闲:尝试CAS获取锁,成功则返回true否則返回false,进入同步队列等待逻辑。 
	 * 		占用:考虑是否是同一个线程重入情况,否則返回false。
	 * 
	 */

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

		final void lock() {
		// 非公平锁:直接CAS闯入式获取锁.
			if (compareAndSetState(0, 1))				setExclusiveOwnerThread(Thread.currentThread());
			else
				acquire(1);// barge失败,常规式获取锁,特点就是在tryAcquire中增加了重入累计的逻辑。详细见AQS代码详解。
		}

		// 实现了AQS的模板方法
		protected final boolean tryAcquire(int acquires) {
			return nonfairTryAcquire(acquires);
		}
	}

ReentrantLock.FairSync.

	/**
	 * 公平模式:实现的AQS模板方法tryAcquire。
	 * 如果当前锁空闲:
	 * 	先判断当前线程是否是等待最久的线程
	 * 	(也就是判断当前线程结点是否是头结点的后继结点,从而保证FIFO公平性):
	 * 	如果是那么就CAS获取锁,失败后返回false,进入AQS-FIFO队列等待
	 * 如果当前锁被占用:考虑是否是同一个线程重入的情况,否则返回false,进入AQS-FIFO队列等待
	 * 公平锁与非公平锁主要区别在于tryAcquire的实现上:
	 *		前者首先会通过CAS强行获取锁(两次CAS尝试),获取失败进入FIFO队列等待;
	 * 	后者获取锁先判断是否“公平”,如果满足“公平”则进行CAS操作。不公平则直接进入FIFO队列等待
	 */
	static final class FairSync extends Sync {
		private static final long serialVersionUID = -3000897897090466540L;

		final void lock() {
			acquire(1);
		}
		protected final boolean tryAcquire(int acquires) {
			final Thread current = Thread.currentThread();
			int c = getState();
			if (c == 0) {// 当前锁空闲
				if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {// !当前线程之前是否还有等待时间更长的结点(也就是說头结点的后继者是否是当前线程)&&CAS获取锁
					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;
		}

总结:
可以看出,ReentrantLock里主要通过AQS实现。
另一篇文章解析了AQS,此处不赘述。

可重入锁:
持有锁的当前线程不变的条件下,通过CAS完成state++,完成锁的重入。每次统一线程重入累加一,这个线程释放一次,减一。直到state==0,锁被彻底释放。

公平与非公平锁
从源码可以看出,所谓"公平"是等待时间较长(头结点的后继者,!hasQueuedPredecessors()判断)的会优先获取锁,
符合FIFO。如果不满足公平性,直接进入等待队列。这会产生较多的线程切换。

而"非公平“则是不考虑线程的等待时间,直接尝试CAS获取锁,自由竞争。

非公平锁可能会导致某个线程长时间处于饥饿中,但是极少线程切换,提升锁的获取成功率,增大系统吞吐量。默认时,ReentrantLock使用非公平锁。

无论公平锁、非公平锁,如果CAS获取失败都会进入FIFO队列,等待再 次获取锁。

ReentrantLock构造器:

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

关于Condition,请查看java并发编程——Condition

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值