Java多线程之ReentrantLock实现原理和源码分析(二)
章节概览、
1、ReentrantLock概述
ReentrantLock字面含义是可重入的互斥锁,实现了和synchronize关键字一样的独占锁功能。但是ReentrantLock使用的是自旋锁,通过CAS硬件原语指令实现的轻量级的锁,不会引起上下文切换。而Synchronize关键字是重量级的且是独占的悲观锁。在使用过程中,会引起上下文切换。同时ReentrantLock增加了一些高级的扩展功能,比如它可以实现公平锁,同时也可以绑定多个Conditon。每个Condition都维护了自己的阻塞队列。而不像wait/notify只维护一个队列而引发的唤醒错误的情况。
2、ReentrantLock的实现模板
ReentrantLock锁保证线程安全的实现模板如下:
class X { private final ReentrantLock lock = new ReentrantLock(); public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() } } }}
从默认的实现模板可以看出,其使用方式和Synchronize不同,首先其不需要同步的对象。而Synchronize需要一个同步锁对象。其次ReentrantLock是显示调用。用户自己可以控制从哪里开始,到哪里结束。而Synchronize只能同步代码块等。具体ReentrantLock的使用方式,请参考:可重入锁:ReentrantLock理解使用
3、ReentrantLock类图结构
ReentrantLock的类图结构如下:
从类的继承图上可以看出,ReentrantLock实现了Lock接口。同时其具有3个内部类,分别为Sync,NonfairSync,FairSync。而Sync有继承了AbstractQueuedSynchronize,这个就是我们常说的AQS。而NonFairSync继承Sync也就是我们所说的非公平锁。FairSync继承Sync实现了公平锁功能。
3.1、Lock接口源码描述
/** * @see ReentrantLock * @see Condition * @see ReadWriteLock * * @since 1.5 * @author Doug Lea */ public interface Lock { //获取锁 void lock(); //获取可中断锁 void lockInterruptibly() throws InterruptedException; //尝试获取锁,立刻返回 boolean tryLock(); //在指定的时间范围内尝试获取锁,立即返回 boolean tryLock(long time, TimeUnit unit) throws InterruptedException; //释放当前锁 void unlock(); //获取一个条件对象,用于线程等待唤醒 Condition newCondition(); }
3.2、ReentrantLock的构造方法和成员
ReentranLock的构造方法成员参数如下:
public class ReentrantLock implements Lock, java.io.Serializable { private static final long serialVersionUID = 7373984872572414699L; // 同步器的引用 private final Sync sync; //非公平锁的构造函数 public ReentrantLock() { sync = new NonfairSync(); } // 公平锁的构造函数 public ReentrantLock(boolean fair) { // 三目运算符,如果为true 则为公平锁,反之为非公平锁 sync = fair ? new FairSync() : new NonfairSync(); } }
3.3、Sync抽象类核心源码分析
// 静态的抽象内部类,修饰符为default,仅能在当前类所在的包中使用 // 继承了AbstractQueuedSynchronizer抽象类 abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; /** * 抽象方法,获取当前锁,具体实现在子类中实现 */ abstract void lock(); /** * 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(); // 获取当前系统的同步状态变量state int c = getState(); // 判断当前的系统状态变量是否为0 // 其中0,表示当前锁空闲。大于0表示当前系统锁已经被占用 if (c == 0) { // 利用CAS原语设置当前state的状态值为1,立刻返回 if (compareAndSetState(0, acquires)) { // 如果设置成功,表示已经获取当前锁,设置当前锁的拥有者为当前线程 setExclusiveOwnerThread(current); // 返回获取成功 return true; } } // 如果当前是state不为0,判断当前线程是否已经拥有锁 // 因为ReentrantLock是可冲入锁,所以如果当前线程已经拥有锁的情况下,可以再次使用该锁 else if (current == getExclusiveOwnerThread()) { // 如果当前线程已经拥有锁,则把当前的 state + 1 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); // 设置当前state的值 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() { // 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 } }
3.4、NonfairSync类核心源码分析
NonfairSync主要是非公平锁的实现:
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. * 实现Sync的方法 */ final void lock() { // 尝试获取锁lock() 方法 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else // 获取锁失败,加入到等待队列 acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
3.5、FairSync类核心源码分析
以下是公平锁实现的核心源码
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; // 公平锁没有尝试的去获取锁的功能,直接调用acquire()方法 final void lock() { acquire(1); } /** * 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) { // 通过hasQueuedPredecessors判断当前线程是否为head的next节点。 // 如果是的话,则尝试获取锁 // 获取锁成功进行一系列的设置 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() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
这里的逻辑判断稍微有些复杂,我们整理下思路。
return返回的代码,可以将逻辑判断分为2部分,只要其中有一个为true,则返回为true。当返回为真的话, if (!hasQueuedPredecessors() && compareAndSetState(0, acquires))就失败。我们详细分析下:
- h != t && ((s = h.next) == null
这个逻辑成立的一种可能是head指向头结点,tail此时还为null。考虑这种情况:当其他某个线程去获取锁失败,需构造一个结点加入同步队列中(假设此时同步队列为空),在添加的时候,需要先创建一个无意义傀儡头结点(在AQS的enq方法中,这是个自旋CAS操作),有可能在将head指向此傀儡结点完毕之后,还未将tail指向此结点。很明显,此线程时间上优于当前线程,所以,返回true,表示有等待中的线程且比自己来的还早。
- s.thread != Thread.currentThread()
当前的线程和Head的next线程是否相等。如果不相等返回为true。说明当前线程不是head的next节点,因为在公平锁中,只有head的next节点才有权利去获取锁
4、 结语
至此ReentrantLock的类结构和核心源码做了简单分析。通过分析我们可以了解到ReentrantLock的整体结构,构造函数,以及当前类的成员变量。以及ReentrantLock的内部类的情况,内部类的继承情况。ReentrantLock中使用了大量的内部类,很好的做到了封装性。对细节实现部分进行了封装。用户只需要调用构造方法,而不用去关系其内部的实现情况。同时也是设计模式中的迪米特法则:最少知道原则的体现。实现了很好的高内聚减少了耦合。
阅读更多- java并发锁ReentrantLock源码分析一 可重入支持中断锁的实现原理
- java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析
- Java多线程之ThreadPoolExecutor实现原理和源码分析(五)
- Java多线程之Condition实现原理和源码分析(四)
- Java多线程之Future实现原理和源码分析(六)
- java.util.concurrent源码分析(三)ReentrantLock实现
- Java并发——重入锁ReentrantLock的实现原理及源码解析
- java多线程-专题-聊聊并发(一)深入分析Volatile的实现原理
- java并发编程之源码分析ThreadPoolExecutor线程池实现原理
- Java多线程 ReentrantReadWriteLock深入分析
- 轻松学习java可重入锁(ReentrantLock)的实现原理
- Java中HashMap底层实现原理(JDK1.8)源码分析
- 轻松掌握java读写锁(ReentrantReadWriteLock)的实现原理
- ReentrantLock实现原理分析
- Java java.util.HashMap实现原理源码分析
- (10) java源码分析 ---- HashMap源码分析 及其 实现原理分析
- java多线程:并发包中ReentrantLock锁的公平锁原理
- java 同步 Synchonized 锁 ReentrantLock 原理 源码
- Java集合ArrayList实现原理——源码分析
- java多线程-专题-聊聊并发(六)ConcurrentLinkedQueue的实现原理分析