概述
ReentrantLock是基于AQS独占模式实现的一种可重入锁,与synchronized不同的是,ReentrantLock提供了公平锁和非公平锁的选择。其本质是基于操作系统的管程实现的。本文就分析一下ReentrantLock的实现原理,由于AQS在AQS-独占模式分析已经介绍过,所以涉及到AQS的地方不会过多介绍,本文会涉及到源码,文章会比较臃肿。
ReentrantLock整体结构
从上图可以看出:
- ReentrantLock实现了Lock接口
- ReentrantLock内部是基于Sync这个抽象类实现的,而Sync继承了AQS,重写了部分AQS的方法
- Sync有两个继承类,FairSync是实现公平锁的,而NonFairSync是实现非公平锁的
下面就分析一下每一块的代码
Sync抽象类
1 //继承AQS 2 abstract static class Sync extends AbstractQueuedSynchronizer { 3 private static final long serialVersionUID = -5179523762034025860L; 4 //子类实现加锁方法,因为有公平锁和非公平锁,所以这里没有实现 5 abstract void lock(); 6 //该方法时tryLock方法调用的,tryLock方法是尝试获取锁,如果没有获取成功,就直接返回 7 //不会阻塞,非公平的方式,直接竞争锁 8 final boolean nonfairTryAcquire(int acquires) { 9 final Thread current = Thread.currentThread(); 10 int c = getState(); 11 //该state是AQS中的,如果为0表示没有别的线程占有锁 12 if (c == 0) { 13 //通过CAS加锁 14 if (compareAndSetState(0, acquires)) { 15 //加锁成功,将持有锁的线程改成当前线程 16 setExclusiveOwnerThread(current); 17 return true; 18 } 19 } 20 //如果持有锁的线程就是当前线程,就是可重入锁,直接修改状态 21 else if (current == getExclusiveOwnerThread()) { 22 int nextc = c + acquires; 23 if (nextc < 0) // overflow 24 throw new Error("Maximum lock count exceeded"); 25 setState(nextc); 26 return true; 27 } 28 //否则加锁失败,直接返回 29 return false; 30 } 31 //释放锁 32 protected final boolean tryRelease(int releases) { 33 int c = getState() - releases; 34 if (Thread.currentThread() != getExclusiveOwnerThread()) 35 throw new IllegalMonitorStateException(); 36 boolean free = false; 37 if (c == 0) { 38 free = true; 39 setExclusiveOwnerThread(null); 40 } 41 setState(c); 42 return free; 43 } 44 //判断当前线程是不是持有锁的线程 45 protected final boolean isHeldExclusively() { 46 // While we must in general read state before owner, 47 // we don't need to do so to check if current thread is owner 48 return getExclusiveOwnerThread() == Thread.currentThread(); 49 } 50 //在AQS中的创建ConditionObject对象,保存等待线程 51 final ConditionObject newCondition() { 52 return new ConditionObject(); 53 } 54 55 final Thread getOwner() { 56 return getState() == 0 ? null : getExclusiveOwnerThread(); 57 } 58 59 final int getHoldCount() { 60 return isHeldExclusively() ? getState() : 0; 61 } 62 63 final boolean isLocked() { 64 return getState() != 0; 65 } 66 67 private void readObject(java.io.ObjectInputStream s) 68 throws java.io.IOException, ClassNotFoundException { 69 s.defaultReadObject(); 70 setState(0); // reset to unlocked state 71 } 72 }
Sync抽象类总结
- 该类主要实现了一个非公平的方式尝试获取锁,如果成功,ok,如果失败,直接返回,不会阻塞或者空转等待
- 实现了一个释放锁的方法,该方法是公平锁和非公平锁公用的方法
- 剩下的都是一些判断的方法,很简单
NonFairSync类分析
1 static final class NonfairSync extends Sync { 2 private static final long serialVersionUID = 7316153563782823691L; 3 //实现加锁方法,ReentrantLock默认就是这个方法,是一个非公平锁 4 final void lock() { 5 if (compareAndSetState(0, 1)) 6 setExclusiveOwnerThread(Thread.currentThread()); 7 else 8 acquire(1); 9 } 10 //这里就是调用上面Sync类中尝试获取锁的方法 11 protected final boolean tryAcquire(int acquires) { 12 return nonfairTryAcquire(acquires); 13 } 14 }
FairSync类分析
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; //公平锁获取锁的实现和上面就不一样,这里没有通过CAS尝试获取锁,而是直接调用acquire方法 final void lock() { acquire(1); } //尝试获取锁 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { //这里就是和非公平锁不同的地方,这里调用了hasQueuedPredecessors,后面会分析这个方法 //剩下的和公平锁的实现就是一样的 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; } }
进入AbstractQueuedSynchronizer #hasQueuedPredecessors()方法
1 public final boolean hasQueuedPredecessors() { 2 Node t = tail; // Read fields in reverse initialization order 3 Node h = head; 4 Node s; 5 //当前队列中不只有一个节点 6 //判断第二个节点是否为空,这个主要是为了防止第三个条件发生空指针异常,因为第一个条件虽然判断了队列中有第二个节点 7 //但是由于是在多线程环境下,随时都有可能第一个节点被干掉,第二个节点变成第一个节点,然后第二个就是null了,再之后就会发生空 8 //指针异常,这个返回的主要是为了判断当前线程是不是队列中的第二个节点,因为队列的第一个节点是虚拟节点,第二个节点才有获取锁的资格 9 //在公平锁的情况下是这样,但是非公平锁就不是这样了 10 return h != t && 11 ((s = h.next) == null || s.thread != Thread.currentThread()); 12 }
总结:公平锁的实现就是严格按照入队的先后顺序获取锁。
Lock接口
public interface Lock { void lock(); //获取可中断锁 void lockInterruptibly() throws InterruptedException; //尝试获取锁 boolean tryLock(); //try获取锁,设置锁最大等待时间,如果超时就取消获取 boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
ReentrantLock构造方法
public ReentrantLock() { sync = new NonfairSync(); } //根据传入参数不同,初始化不同的类 public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
ReentrantLock和Synchronized对比
- 与
synchronized
相比,ReentrantLock提供了更多,更加全面的功能,具备更强的扩展性。例如:时间锁等候,可中断锁等候 - ReentrantLock 还提供了条件 Condition ,对线程的等待、唤醒操作更加详细和灵活,所以在多个条件变量和高度竞争锁的地方,ReentrantLock 更加适合。
- ReentrantLock 支持中断处理,且性能较
synchronized
会好些。 - ReentrantLock实现了公平锁和非公平锁的机制,而synchronized只有非公平一种模式
- synchronized不需要手动释放锁,但是ReentrantLock释放锁的时候,需要程序员自己释放
- ReentrantLock和synchronized都是通过内存屏障来保证有序性、可见性的,只不过ReentrantLock是volatile实现的,而synchronized不是使用volatile,但是volatile和synchronized在保证有序性和可见性上使用的内存屏障是一样的,所以也就是说两者基本相同
总结
本文介绍ReentrantLock源码结构以及实现原理,涉及到AQS的部分没有详细说明,主要是另一个篇文章已经介绍过,最后介绍了ReentrantLock和sychronized的区别。
参考:
【死磕 Java 并发】—– J.U.C 之重入锁:ReentrantLock