1. 概况
ReentrantLock 是可重入的,也就是说如果当前线程拥有这把锁,进入到下一个加了同样锁的方法中时,不用等待直接获取,只是对象头字段增加了一个标记,标记进入了两次。
比 Synchronized 更加灵活,多一个尝试获取锁的办法,和获取锁超时时间
2. 类定义
public class ReentrantLock implements Lock, java.io.Serializable {}
ReentrantLock 实现 Lock 接口,Lock 接口包含包含以下几个方法:
- lock(),获取锁,当前线程未获取成功时会进入等待状态
- lockInterruptibly(),可中断的获取锁
- boolean tryLock(),尝试获取锁,如果当前线程没有获取到锁,会立马返回 false
- boolean tryLock(long time, TimeUnit unit) throws InterruptedException,尝试获取锁,指定时间还没有获取到,或者被中断,返回 false
- unlock(),释放锁
- newCondition(),返回此锁对象对应的条件实例,一般来说 Condition 和 Lock 是配合使用的
3. 成员变量
没有定义成员变量
4. 构造方法
默认情况下 ReentrantLock 是 非公平锁
public ReentrantLock() {sync = new NonfairSync();}public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}
5. 成员方法
// 具体的加锁由对应的实例去实现,实际调用的是 tryAquire 方法,默认情况是 非公平锁的 lock 方法public void lock() {sync.lock();}// 可中断的加锁办法,实际调用 AQS 中的 acquireInterruptibly 方法public void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}// 以非公平的方式获取锁public boolean tryLock() {return sync.nonfairTryAcquire(1);}// 释放锁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;}// 获取当前锁对应的线程,可能为 nullprotected 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 Collection<Thread> getQueuedThreads() {return sync.getQueuedThreads();}// 查询是否有线程 正在等待与此锁定有关的condition条件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);}// 返回与此锁有关的condition,由于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<Thread> 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);}public String toString() {Thread o = sync.getOwner();return super.toString() + ((o == null) ?"[Unlocked]" :"[Locked by thread " + o.getName() + "]");}
内部类
ReentranLock 中的成员方法,基本是由继承于 AbstractQueuedSynchronizer 的内部类 Sync 的子类 FairSync 和 NonfairSync 实现的,所以弄懂 Sync 子类方法的实现,就基本弄懂了 ReentranLock 的整个源码。
- Sync 是 ReentrantLock 的内部抽象类,继承于 AbstractQueuedSynchronizer
- FairSync 和 NonfairSync 是 Sync 的子类,在父类的基础上,重写了 lock(),tryAquire() 方法,从而使得 ReentranLock 具有 XXX 特性。
abstract static class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = -5179523762034025860L;// 抽象的 获取锁方法,供 Sync 的子类 FairSync 和 NonfairSync 使用abstract void lock();// 非公平方式获取锁final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) { // 若没有锁竞争if (compareAndSetState(0, acquires)) { // 若 state == 0,则以 CAS 方式将 state 设为 acquiressetExclusiveOwnerThread(current); // 获取成功后,当前线程就具备这些资源的锁return true;}}else if (current == getExclusiveOwnerThread()) { // 当前线程已经拥有这个锁int nextc = c + acquires; // 重入的次数进行累加if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded"setState(nextc); // 设定 statereturn true;}return false;}// 尝试释放锁,若当前线程释放之后不握有锁,返回 true,否则返回 falseprotected final boolean tryRelease(int releases) {int c = getState() - releases; // 释放 releases 个锁之后,还剩下 c 个锁if (Thread.currentThread() != getExclusiveOwnerThread()) // 如果当前线程不拥有这个锁,抛出异常throw new IllegalMonitorStateException();boolean free = false;if (c == 0) { // 释放 releases 个锁之后,当前线程握有 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 ownerreturn getExclusiveOwnerThread() == Thread.currentThread();}final ConditionObject newCondition() {return new ConditionObject();}// Methods relayed from outer classfinal 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}}
FairSync
实现父类未实现的 lock 方法,实际调用的是 AQS 的 acquire 方法,而 AQS 的 acquire 在 FairSync 中又进行了重写实现。
相比非公平锁,公平锁的 tryAcquire 的实现就多了一个 hasQueuedPredecessors
static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;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) {// 阻塞队列中没有比当前线程等待更久的线程 并且 state 的值是 0 时,以 CAS 方式获取独占锁并返回 trueif (!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;}}
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.*/final void lock() {if (compareAndSetState(0, 1)) // 上来直接判断 state,若没有竞争,直接加锁setExclusiveOwnerThread(Thread.currentThread());elseacquire(1); // 否则和 Fair.lock 一样调用 AQS 的 aquire 方法}protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires); // 以非公平形式获取锁}}
AQS 的 acquire 方法,这个方法大体意思:
- 直接以 CAS 方式先尝试获取锁,获取成功就直接返回
- 获取失败,那么添加到队列末尾,添加成功,直接返回
- 若队列末尾添加失败,该线程就进行中断
AbstractQueuedSynchronizer.javapublic final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}static void selfInterrupt() {Thread.currentThread().interrupt();}// 当队列为空 或者 队列中有比当前线程等待更久的线程时,返回 truepublic 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 orderNode h = head;Node s;return h != t &&((s = h.next) == null || s.thread != Thread.currentThread());}
AQS 中的方法
public final void acquireInterruptibly(int arg)throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();if (!tryAcquire(arg)) // tryAcquire 由子类去实现,去获取 arg 个锁,获取失败就抛出异常doAcquireInterruptibly(arg);}
ReentrantLock 与 Synchronized 区别
ReentrantLock 显示的获得、释放锁,synchronized 隐式获得释放锁
ReentrantLock 可响应中断、可轮回,synchronized 是不可以响应中断的,为处理锁的不可用性提供了更高的灵活性
ReentrantLock 是 API 级别的,synchronized 是 JVM 级别的
ReentrantLock 可以实现公平锁
ReentrantLock 通过 Condition 可以绑定多个条件
底层实现不一样, synchronized 是同步阻塞,使用的是悲观并发策略,lock 是同步非阻塞,采用的是乐观并发策略
Lock 是一个接口,而 synchronized 是 Java 中的关键字,synchronized 是内置的语言实现。
synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而 Lock 在发生异常时,如果没有主动通过 unLock() 去释放锁,则很可能造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁。
Lock 可以让等待锁的线程响应中断,而 synchronized 却不行,使用 synchronized 时,等待的线程会一直等待下去,不能够响应中断。
通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
Lock 可以提高多个线程进行读操作的效率,既就是实现读写锁等。
7. 总结
- ReentrantLock 的 tryLock() 方法是非公平的,因为无论在什么模式下,该方法都会调用抽象同步器的 nonfairTryAcquire() 方法
