源码角度来分析ReentrantLock是怎么利用AQS来实现公平锁,和非公平锁
来源:互联网 发布:真正刷钻软件 编辑:程序博客网 时间:2024/06/06 02:53
ReentrantLock
这篇文章是从JDK8的ReentrantLock源码角度来分析ReentrantLock是怎么利用AQS来实现公平锁,和非公平锁的。
所以前提需要理解AQS。
简单的翻译了一下,大概清楚JDK doc里ReentrantLock介绍
/** * A reentrant mutual exclusion {@link Lock} with the same basic * behavior and semantics as the implicit monitor lock accessed using * {@code synchronized} methods and statements, but with extended * capabilities. ReentrantLock属于非共享锁,即独占锁,每次只能是一个线程取得锁资源,它与synchronized关键字具有相同的锁语义,但是ReentrantLock具有一些另外的拓展功能 * * A {@code ReentrantLock} is owned by the thread last * successfully locking, but not yet unlocking it. A thread invoking * {@code lock} will return, successfully acquiring the lock, when * the lock is not owned by another thread. The method will return * immediately if the current thread already owns the lock. This can * be checked using methods {@link #isHeldByCurrentThread}, and {@link * #getHoldCount}. ReentrantLock锁只属于获得它的线程,直到unlock解锁。如果一个线程执行lock(),如果当前锁没有 其他线程在占用,则会获取得锁,立即返回。 如果该线程已经拥有当前锁,再执行lock(),也会再次获得锁,立即返回。 当前线程是否持有当前锁,可以用锁的isHeldByCurrentThread()来查看获得当前锁的线程。 * * <p>The constructor for this class accepts an optional * <em>fairness</em> parameter. When set {@code true}, under * contention, locks favor granting access to the longest-waiting * thread. Otherwise this lock does not guarantee any particular * access order. ReentrantLock的构造函数可以传入一个boolean类型的true,来表示构造一个公平的锁,此时锁倾向于最长 等待时间的线程。 如果默认。或者传入函数为false。则为非公平锁,此时不保证获取得到锁的线程顺序。 Programs using fair locks accessed by many threads * may display lower overall throughput (i.e., are slower; often much * slower) than those using the default setting, but have smaller * variances in times to obtain locks and guarantee lack of * starvation. 使用公平锁的吞吐率其实在某些情况会比使用非公平锁低,但是可以解决饥饿现象 Note however, that fairness of locks does not guarantee * fairness of thread scheduling. Thus, one of many threads using a * fair lock may obtain it multiple times in succession while other * active threads are not progressing and not currently holding the * lock. * Also note that the untimed {@link #tryLock()} method does not * honor the fairness setting. It will succeed if the lock * is available even if other threads are waiting. 值得注意的是,tryLock()属于非阻塞方法,成功则返回true,失败则返回false, 这个方法无所谓的公平和非公平之分,即使是设置的是公平锁,任何线程调用这个方法,都会尝试获得锁资源, 即使等待队列中有其他线程正在等待该锁资源 * * <p>It is recommended practice to <em>always</em> immediately * follow a call to {@code lock} with a {@code try} block, most * typically in a before/after construction such as: * * <pre> {@code * class X { * private final ReentrantLock lock = new ReentrantLock(); * // ... * * public void m() { * lock.lock(); // block until condition holds * try { * // ... method body * } finally { * lock.unlock() * } * } * }}</pre> * * <p>In addition to implementing the {@link Lock} interface, this * class defines a number of {@code public} and {@code protected} * methods for inspecting the state of the lock. Some of these * methods are only useful for instrumentation and monitoring. * * <p>Serialization of this class behaves in the same way as built-in * locks: a deserialized lock is in the unlocked state, regardless of * its state when serialized. * * <p>This lock supports a maximum of 2147483647 recursive locks by * the same thread. Attempts to exceed this limit result in * {@link Error} throws from locking methods. 同一线程最大可重入锁的次数是 2的31次方减1,就是2147483647 * * @since 1.5 * @author Doug Lea */
IDEA里面的UML图显示,ReentranLock里面组合了Sync内部类,而Sync是继承了AQS。
其次,NonfairSync,fairSync是集成自Sync的两个内部类,分别对应非公平与公平的AQS。
再仔细看,NonfairSync,fairSync只是重写了Sync的lock()方法和重写了AQS的tryAcquire()
首先是,构造函数
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
再来看看内部继承AQS的同步器Sync,Sync作为ReentrantLock的内部类。
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(); // NonfairSync,fairSync 要继承此Sync要实现的 /** * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. 注意这里,因为Lock接口的语义里,tryLock()是不分公平与非公平的,(实际语义其实等同于非公平的) 所以初始构造ReentrantLock时候,无论是new的fairSync还是notfairSync,都应该可以执行tryLock(), 因此tryLock()用到的Sync(AQS)的nonfairTryAcquire(),应该在NonfairSync,fairSync父类中实现,就是此类Sync,这样才可以达到共用的效果。 */ final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { //CAS安全地更新state 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); //此时是重入,所以这里可以不用CAS来改变state return true; } return false; } //此tryRelease为NonfairSync,fairSync共用,因为释放无所谓公平不公平 protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { //state==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 } }
再来看看 NonfairSync ,也就是默认的非公平模式
/** * Sync object for non-fair locks */ static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ @Override //重写父类Sync的抽象方法Lock(),其实公平和非公平,区别在这,非公平lock()的时候,都会先尝试获取锁资源,失败了,再进入acquire()流程。 //而公平锁却直接进入acquire()流程,需要检验等待队列中是否有其他线程在等待,若无,才尝试获取锁 final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } //直接调用的是Sync定义的nonfairTryAcquire() protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
再来看FairSync
/** * Sync object for fair locks */ 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) { 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; } }
所以说,非公平锁,lock的时候,会无视正在等待锁资源的队列里面是否有成员,而直接尝试一次获取,若不成功,则还是会进入AQS的CLH等待队列,然后阻塞,顺序等待唤醒,获取。
公平锁,lock的时候,则不能无视正在等待锁资源的队列里面的成员。
最后,看下ReentrantLock的提供给我们使用的接口函数,是怎样调用fairSync,nonfairSync的
public void lock() { sync.lock();}
分派给NonfairSync的lock()
final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
分派给fairSync的lock()
final void lock() { acquire(1); }
而tryLock(),直接是用的sync内部类定义的nonfairTryAcquire(),这也是为什么nonfairTryAcquire()定义在Sync,而不是NonfairSync()的原因。
public boolean tryLock() { return sync.nonfairTryAcquire(1); }
总结,Doug Lea,非常巧妙的利用AQS的模板方法,简洁而清晰地实现了具有公平和非公平的模式的ReentrantLock,值得我们去学习。
阅读全文
1 0
- 源码角度来分析ReentrantLock是怎么利用AQS来实现公平锁,和非公平锁
- ReentrantLock与公平锁、非公平锁实现
- ReentrantLock与公平锁、非公平锁实现
- ReentrantLock公平锁与非公平锁
- java之ReentrantLock公平锁和非公平锁
- Java中ReentrantLock的公平锁和非公平锁
- ReentrantLock的公平锁和非公平锁
- 深入剖析ReentrantLock公平锁与非公平锁源码实现
- 深入剖析ReentrantLock公平锁与非公平锁源码实现
- ReentrantLock之公平锁与非公平锁浅析
- ReentrantLock(三):公平锁与非公平锁
- 4.1.9ReentrantLock的公平锁与非公平锁
- Java中的公平锁和非公平锁实现详解
- 第五章 ReentrantLock源码解析1--获得非公平锁与公平锁lock()
- 第五章 ReentrantLock源码解析1--获得非公平锁与公平锁lock()
- 公平锁和非公平锁
- 公平锁和非公平锁
- 公平锁和非公平锁
- Interger
- 谷歌研究发现优秀的团队必须具备这五个关键特质
- CSS学习笔记
- hive基础(二)--HQL语句
- WatchOS系统开发大全(4)-WatchApp生命周期
- 源码角度来分析ReentrantLock是怎么利用AQS来实现公平锁,和非公平锁
- Error response from daemon: rpc error: code = 14 desc = grpc: the connection is unavailable
- android jni传long型数据给java
- JSON转换成JsonObject的方法---用 Gson
- SQL Server将数据库的用户名映射到登录名
- Loaded plugins: fastestmirror, refresh-packagekit, security Loading mirror speeds from cached hostfi
- Java编程:深入理解final关键字
- C语言-预处理指令(Preprocessing Directive)-宏定义
- 玩转大数据_你需要了解这8种项目类型!