Jdk1.6 JUC源码解析(7)-locks-ReentrantLock
作者:大飞
功能简介:
- Java代码层面提供的锁机制,可做为Synchronized(jvm内置)的替代物,和Synchronized一样都是可重入的。
- 与Synchronized相比较而言,ReentrantLock有以下优势:支持公平/非公平锁、支持可中断的锁、支持非阻塞的tryLock(可超时)、支持锁条件、可跨代码块使用(一个地方加锁,另一个地方解锁),总之比Synchronized更加灵活。但也有缺点,比如锁需要显示解锁、无法充分享用JVM内部性能提升带来的好处等等。
源码分析:
- ReentrantLock实现了Lock接口,先来看下这个接口:
public interface Lock { /** * 获取锁,如果锁无法获取,当前线程被阻塞,直到锁可以获取并获取成功为止。 */ void lock(); /** * 在当前线程没有被中断的情况下获取锁。 * * 如果获取成功,方法结束。 * * 如果锁无法获取,当前线程被阻塞,直到下面情况发生: * * 1.当前线程(被唤醒后)成功获取锁。 * 2.当前线程被其他线程中断。 */ void lockInterruptibly() throws InterruptedException; /** * 如果当前锁是可用的,获取锁。 * * 获取成功后,返回true。 * 如果当前锁不可用,返回false。 */ boolean tryLock(); /** * 如果锁在给定超时时间内可用,并且当前线程没有被中断,那么获取锁。 * * 如果锁可用,获取锁成功并返回true。 * * 如果锁无法获取,当前线程被阻塞,直到下面情况发生: * * 1.当前线程(被唤醒后)成功获取锁。 * 2.当前线程被其他线程中断。 * 3.指定的等待时间超时。 */ boolean tryLock(long time, TimeUnit unit) throws InterruptedException; /** * 释放锁。 */ void unlock(); /** * 返回一个和当前锁实例相关联的条件。 * * 当前线程必须首先获取锁后才能在锁条件上等待。 * 一个Condition的await()方法调用会在等待之前自动释放锁,在等待结束 * 前重新获取锁。 */ Condition newCondition(); }
- 之前分析AQS的时候提到过,基于AQS构建的同步机制都会使用内部帮助类继承AQS的方式构建,看下ReentrantLock中的同步机制:
//内部同步机制的引用。 private final Sync sync; /** * 这个锁实现的基本同步控制机制,下面会提供公平和非公平版本的子类。 * 利用AQS的state来表示锁持有(重入)的次数。. */ static abstract 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(); /** * 方法用来支持非公平的tryLock */ final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { //如果当前没有任何线程获取锁(锁可用),尝试设置state。 if (compareAndSetState(0, acquires)) { //如果设置成功,将当前线程信息设置到AQS中(所有权关联)。 setExclusiveOwnerThread(current); return true; } } //如果锁已经被持有,那么判断一下持有锁的线程是否为当前线程。 else if (current == getExclusiveOwnerThread()) { //如果是当前线程在持有锁,那么这里累计一下重入次数。 int nextc = c + acquires; if (nextc < 0) // overflow 重入次数最大不能超过int的最大值 throw new Error("Maximum lock count exceeded"); //设置到AQS的state中 setState(nextc); return true; } //如果锁已经被持有,且持有线程不是当前线程,返回false。 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) { //如果当前线程完全释放了锁(重入次数为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 this lock instance from a stream. * @param s the stream */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); setState(0); // reset to unlocked state } }
接下来先看一下非公平版本的子类:
/** * Sync object for non-fair locks */ final static 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() { //这里首先尝试一个短代码路径,直接CAS设置state,尝试获取锁。 //相当于一个插队的动作(可能出现AQS等待队列里有线程在等待,但当前线程竞争成功)。 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1);//如果CAS失败,调用AQS的独占请求方法。 } protected final boolean tryAcquire(int acquires) { //调用上面父类的nonfairTryAcquire方法。 return nonfairTryAcquire(acquires); } }
再来先看一下公平版本的子类:
/** * Sync object for fair locks */ final static class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } /** * 公平版本的tryAcquire。 * 只有在递归(重入)或者同步队列中没有其他线程 * 或者当前线程是等待队列中的第一个线程时才准许访问。 */ protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { //如果当前锁可用,且同步等待队列中没有其他线程,那么尝试设置state 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.当前线程首先会无条件的执行一个CAS操作来获取锁,如果CAS操作成功,获取锁成功。
2.如果第1步没成功,当前会检查锁是否被其他线程持有,也就是锁是否可用。
3.如果没有其他线程持有锁,会以CAS的方式尝试获取锁,如果CAS操作成功,获取锁成功。
4.如果有其他线程持有锁,会判断一下持有锁的线程是否为当前线程,如果是当前线程,重入次数+1,获取锁成功。
5.根据AQS的分析,上述2、3、4步会执行多次,如果最终获取锁失败,当前线程会被阻塞,等待其他线程执行解锁操作将其唤醒。
公平版的锁-加锁操作
1.当前线程首先会检查锁是否被其他线程持有,并且当前同步等待队列里有没有其他线程在等待。
2.如果没有其他线程持有锁,且同步等待队列里没有其他线程,会以CAS的方式尝试获取锁,如果CAS操作成功,获取锁成功。
3.如果有其他线程持有锁,会判断一下持有锁的线程是否为当前线程,如果是当前线程,重入次数+1,获取锁成功。
4.根据AQS的分析,上述1、2、3步会执行多次,如果最终获取锁失败,当前线程会被阻塞,等待其他线程执行解锁操作将其唤醒。
非公平版和公平版锁的解锁操作一样
1.当前线程首先将锁重入次数减1(AQS的state),如果减1后结果为0,将当前同步器的线程信息置空,并唤醒同步等待队列中队头的等待线程。
2.如果第1步中,重入次数减1后结果不为0(说明当前线程还持有当前锁),方法结束。
- 有了内部的基础同步机制,ReentrantLock的实现就很简单了,直接看代码:
/** * 默认情况下构建非公平锁。 */ public ReentrantLock() { sync = new NonfairSync(); } /** * 根据给定的公平策略生成相应的实例。 * * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantLock(boolean fair) { sync = (fair)? new FairSync() : new NonfairSync(); } public void lock() { sync.lock(); } public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } public boolean tryLock() { return sync.nonfairTryAcquire(1); } public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } public void unlock() { sync.release(1); } public Condition newCondition() { return sync.newCondition(); }
最后看一下一些支持监测的方法:
/** * 获取当前线程的对当前锁的持有(重入)次数。 */ public int getHoldCount() { return sync.getHoldCount(); } /** * 判断当前锁是否被当前线程持有。 */ public boolean isHeldByCurrentThread() { return sync.isHeldExclusively(); } /** * 判断当前锁是否被(某个线程)持有。 */ public boolean isLocked() { return sync.isLocked(); } /** * 当前锁是否为公平锁。 */ public final boolean isFair() { return sync instanceof FairSync; } /** * 获取持有当前锁的线程。 */ protected Thread getOwner() { return sync.getOwner(); } /** * 判断是否有线程在当前锁的同步等待队列中等待。 */ public final boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } /** * 判断给定的线程是否在当前锁的同步等待队列中等待。 */ public final boolean hasQueuedThread(Thread thread) { return sync.isQueued(thread); } /** * 获取当前锁的同步等待队列中的等待线程(估计)数量。 */ public final int getQueueLength() { return sync.getQueueLength(); } /** * 获取当前锁的同步等待队列中等待的线程。 */ protected CollectiongetQueuedThreads() { return sync.getQueuedThreads(); } /** * 判断是否有线程在给定条件的条件等待队列上等待。 */ public boolean hasWaiters(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition); } /** * 获取给定条件的条件等待队列中等待线程的(估计)数量。 */ public int getWaitQueueLength(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition); } /** * 获取给定条件的条件等待队列中等待线程。 */ protected Collection getWaitingThreads(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition); }
ReentrantLock的代码解析完毕!
参见:Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer