eentrantLock是一个可重入的互斥锁(独占锁),它和隐式锁(使用Synchronized修饰的方法)一样有着相同的功能和语义。但是ReentrantLock还扩展了一些特别功能。
ReentrantLock属于最后一个成功lock的线程,且在该线程未释放(unlock)之前。
成功获取lock分为下面两种情况
1.在当前没有任何其他线程拥有锁(lock过且未unlock)情况下,一个线程调用lock立马返回结果(不阻塞)
2.从名字我们也可以知道Reentrant是可重入的意思,如果当前线程已经lock过,那么它再次lock时候也是立马返回结果(不阻塞)。可以使用方法 isHeldByCurrentThread, and getHoldCount得知当前线程是否已经lock过和lock重入的次数
ReentrantLock提供两种重入锁机制
1.公平锁。
描述:公平锁即lock时候,如果AQS同步等待队列里有同样在等待lock的线程,那么该次lock会进入AQS同步等待队列的尾节点,阻塞排队依次等待lock.
2.非公平锁。
描述:非公平锁即lock时候,调用CAS指令抢占式的更新状态值(可能同时多个线程共同竞争CAS更新状态值,也可能和AQS同步队列的头节点共同竞争更新值),如果成功更新状态值,则成功获取lock,否则阻塞排队等待lock.
两种锁的对比:公平锁的总吞吐量要低于非公平锁。
既然ReentrantLock提供了两种机制的重入锁,那么对于用户来说肯定也得有相对应的获取方法。
ReentrantLock有两个构造函数:
1.无参构造(默认是非公平锁,性能好)
2.带有fairness 布尔值的形参的构造函数。
fairness = true :公平锁
fairness = false :非公平锁
注意:在ReentrantLock里,有个比较特殊的方法tryLock(),它总是使用非公平锁的lock方法。即无论我们选择哪种机制锁,在调用tryLock时候,总是使用非公平锁的lock方法。
在使用ReentrantLock时候,需要注意使用方式。可以参考下面的使用方式:
class X {
private final ReentrantLock lock = new ReentrantLock();
// …
public void m() {
lock.lock(); // block until condition holds
try {
// … method body
} finally {
//一定要记住unlock!!!
lock.unlock()
}
}
}
如果对ReentrantLock序列化,那么当反序列化时候,得到的总是unLock状态(即使已经lock,序列化再反序列化,还能成功lock)
由于ReentrantLock使用的AbstractQueueSynchronized,该AQS使用的是int类型的state。所以ReentrantLock最多支持int值范围2147483647次的重入次数.
想要更多的重入次数可以使用AbstractQueuedLongSynchronizer来仿造个ReentrantLock。AbstractQueuedLongSynchronizer使用的是long型的state,支持long范围内的state。
下面看看ReentrantLock的源码部分。ReentrantLock内部实现了个Sync继承于AQS,整个ReentrantLock几乎都只是代理了Sync方法。如果你熟悉AQS的话,随便翻下ReentrantLock源码即可,没必要看那些英文。
如果读者未了解的AQS的话,建议先了解AQS。
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
/**
该ReentrantLock 实现同步机制的基础类。Sync 有两个版本子类(公平和非公平)。
ReentrantLock 主要使用AQS的state值来控制独占锁的。
**/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
//主要原因是因为非公平锁需要CAS快速尝试(相对应公平锁)
abstract void lock();
/**
这个方法相当于AQS的ryAcquire,为什么非公平版的TryAcquire代码需要写在父类Sync 这里?
前面也说到过需要注意tryLock,它是只调用非公平版的tryLock。而ReentrantLock 是代理了
Sync ,根据多态机制。所以非公平版的TryAcquire只能写在父类Sync这里了。
**/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//如果c==0,代表当前没任何线程持有锁(lock过)
if (c == 0) {
//CAS更新状态值
if (compareAndSetState(0, acquires)) {
//更新成功,代表成功获取锁,设置当前线程到OwnerThread
setExclusiveOwnerThread(current);
return true;
}
}
//如果当前线程已经拥有锁,则重入次数+acquires(隐式锁Synchronized也具有重入语义)
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
//释放状态releases
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
//必须得拥有锁才能释放锁!
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//注意这里得c==0才代表释放成功
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();
}
//相当于Object的wait 、signal、signalAll功能,但是更加灵活强大,比如一个独占锁可以拥有多个条件谓词
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
}
}
//非公平版的Lock
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() {
//尝试CAS抢占修改state值(无视AQS等待队列),如果成功则立马返回,否则阻塞等待了。。
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
//公平版的Lock
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) {
//这里多了个hasQueuedPredecessors,即判断当前AQS同步等待队列中是否有正在等待的节点
//如果存在,则添加返回false,添加到AQS同步等待队列依次等待lock.所有这里会造成吞吐量的损耗
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;
}
}
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
/**
这里仅仅只是代理了sync.lock();。
如果没有其他线程拥有锁,那么该调用该方法会立即返回(成功获取锁),且设置当前线程的锁次数为1。
如果当前线程已经成功获取过锁,那么调用该方法会设置 锁次数+1,然后立即返回。
如果锁已经被其他线程所获取,那么当前线程会被加入到AQS的同步等待队列,且挂起当前线程,直到
它被唤醒再次成功获取锁,然后设置锁次数为1返回
**/
public void lock() {
sync.lock();
}
/**
* Acquires the lock unless the current thread is
* {@linkplain Thread#interrupt interrupted}.
*
* <p>Acquires the lock if it is not held by another thread and returns
* immediately, setting the lock hold count to one.
*
* <p>If the current thread already holds this lock then the hold count
* is incremented by one and the method returns immediately.
*
* <p>If the lock is held by another thread then the
* current thread becomes disabled for thread scheduling
* purposes and lies dormant until one of two things happens:
*
* <ul>
*
* <li>The lock is acquired by the current thread; or
*
* <li>Some other thread {@linkplain Thread#interrupt interrupts} the
* current thread.
*
* </ul>
*
* <p>If the lock is acquired by the current thread then the lock hold
* count is set to one.
*
* <p>If the current thread:
*
* <ul>
*
* <li>has its interrupted status set on entry to this method; or
*
* <li>is {@linkplain Thread#interrupt interrupted} while acquiring
* the lock,
*
* </ul>
*
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* <p>In this implementation, as this method is an explicit
* interruption point, preference is given to responding to the
* interrupt over normal or reentrant acquisition of the lock.
*
* @throws InterruptedException if the current thread is interrupted
*/
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
/**
在调用tryLock方法时候,如果锁未被其他线程所占有,且该方法返回的值返回true,那么则代表成功获取锁。
无论当前的ReentrantLock使用的是公平排队机制还是有其他线程在AQS的等待队列等待获取锁,在调用该
方法时候也会立马返回CAS竞争获取锁的结果。 即使该方法打破了公平机制,但是他在某些情况下是有用的。
如果想使用公平机制的tryLock的话,那么可以通过使用tryLock(0, TimeUnit.SECONDS)来达到公平机制。
看下面tryLock(0, TimeUnit.SECONDS)代码就明白了
**/
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
/**
该方法和tryLock()差不多,都是得到一个布尔值来判断是否成功获取锁。
tryLock(long timeout, TimeUnit unit)相对无参的tryLock不同之处
1. tryLock(long timeout, TimeUnit unit)可以支持指定的一个时间段内等待成功获取锁。如果遇到下面三种情况,则立即返回
1)已经成功获取锁
2)该线程已被其他线程中断
3)设置的等待时间已经到达。
2. tryLock(long timeout, TimeUnit unit)可以根据当前ReentrantLock使用的公平或者非公平机制来获取锁
**/
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
/**
尝试释放当前锁。
如果当前线程拥有锁,则该锁的次数-1。如果锁的state-1后,值为0.那么则代表当前线程完全释放锁成功。
如果当前线程不拥有锁,那么则会抛出IllegalMonitorStateException异常。
**/
public void unlock() {
sync.release(1);
}
/**
获取一个属于当前Lock实例的Condition实例。
该Condition有着和在使用内置锁时候的Object的监视器方法(wait,notify,notifyAll)一样的功能。
如果当前线程没有拥有锁,那么在调用Condition的await,signal这些方法时候回抛出IllegalMonitorStateException异常
Condition队列中的节点是根据FIFO的结构来signall节点的
当调用Condition的await方法时候,当前锁会被释放(清除state)且保存被释放的state值,然后挂起当前线程,直到当前
线程被signal,此时在该方法结束返回前会恢复先前保存的state值。
如果在await期间,发现线程已被中断,那么则会终止当前的await。抛出InterruptedException异常且清除线程的interrupted 状态值
**/
public Condition newCondition() {
return sync.newCondition();
}
/**
获取当前线程获取锁的次数
**/
public int getHoldCount() {
return sync.getHoldCount();
}
/**
判断当前线程是否拥有锁
**/
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
/**
* Queries if this lock is held by any thread. This method is
* designed for use in monitoring of the system state,
* not for synchronization control.
*
* @return {@code true} if any thread holds this lock and
* {@code false} otherwise
*/
public boolean isLocked() {
return sync.isLocked();
}
//判断是否是公平锁。 true==公平锁
public final boolean isFair() {
return sync instanceof FairSync;
}
/**
获取当前拥有锁的线程。如果没有任何线程拥有,则返回null。
当调用这个方法时候,由于在并发情况下,AQS只能尽可能获取正确的结果。
因为有可能其他线程刚刚修改完state成功,还未来得及设置其他线程到own里面去。
所以这时我们获取的话那么就可能为null。
**/
protected Thread getOwner() {
return sync.getOwner();
}
/**
查询是否有节点(线程)在AQS队列中等待获取锁。由于队列中等待的节点可能随时因为一些
情况而被取消,所以即使返回true,也不代表队列中的节点(线程)最终能获取到锁。
**/
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
/**
查询给定的线程thread是否在AQS等待队列中,如果在则返回true,否则false
**/
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
/**
获取当前在AQS同步队列中等待获取锁的节点(线程)个数的近似值。由于是通过迭代
AQS同步队列来计算节点个数的,在迭代过程中,节点结构有可能修改,因此得到的只能
是近似值。
**/
public final int getQueueLength() {
return sync.getQueueLength();
}
/**
获取在AQS同步队列中等待获取锁的线程的集合。由于是通过迭代AQS同步队列来收集线程的,
在迭代过程中,节点结构有可能修改,因此得到的只能是近似值。
**/
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
/**
查询当前独占锁下的条件队列中是否有节点(即当前独占锁下的Condition是否有线程在await)
**/
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是否有线程在await)的个数.
也是近似值
**/
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);
}
/**
获取当前独占锁下的条件队列中节点(即当前独占锁下的Condition是否有线程在await)的线程集合
**/
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);
}
/**
* Returns a string identifying this lock, as well as its lock state.
* The state, in brackets, includes either the String {@code "Unlocked"}
* or the String {@code "Locked by"} followed by the
* {@linkplain Thread#getName name} of the owning thread.
*
* @return a string identifying this lock, as well as its lock state
*/
public String toString() {
Thread o = sync.getOwner();
return super.toString() + ((o == null) ?
"[Unlocked]" :
"[Locked by thread " + o.getName() + "]");
}
}