JUC之Lock、ReentrantLock可重入独占锁
前言
ReentrantLock即可重入锁,实现了Lock和Serializable接口
在java环境下ReentrantLock和Synchronized都是可重入锁
ReentrantLock构造函数中提供两种锁:创建公平锁和非公平锁(默认)
- ReentrantLock有三个内部类 Sync、NonfairSync和FairSync类。
- Sync继承AbstractQueuedSynchronized抽象类
- NonfairSync(非公平锁)继承Sync抽象类。
- FairSync(公平锁)继承Sync抽象类。
公平锁,线程按照发出的请求的顺序获取锁;非公平锁,允许插队。
公平锁上,若有另外一个线程持有锁或有其他线程在等待队列中等待这个锁,那么新发出的请求的线程将被放入到对类中。
非公平锁性能高于公平锁性能的原因
- 在恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟
- 假设线程 A 持有一个锁,并且线程 B 请求这个锁。由于锁被 A 持有,因此 B 将被挂起。当 A 释放锁时,B 将被唤醒,因此 B 会再次尝试获取这个锁。与此同时,如果线程 C 也请求这个锁,那么 C 很可能会在 B 被完全唤醒之前获得、使用以及释放这个锁。这样就是一种双赢的局面,B 获得锁的时刻并没有推迟,C 更早的获得了锁,并且吞吐量也提高了。
当持有锁的时间相对较长或者请求锁的平均时间间隔较长,应该使用公平锁
公平锁保证了锁的获取按照FIFO原则,而代价是进行大量的线程切换。非公平锁虽然可能造成线程“饥饿”,但极少的线程切换,保证了其更大的吞吐量。
源码
ReentrantLock类是实现了Lock接口,先看看接口里的抽象方法
Lock API
void lock(); //获取锁
void lockInterruptibly() throws InterruptedException; //如果当前线程未被中断,则获取锁
boolean tryLock(); //只有锁在空闲状态时才能获取该锁。
boolean tryLock(long time, TimeUnit unit) throws InterruptedException; //锁在给定时间内空闲,且当前线程未被中断,则获取锁
void unlock(); //释放锁
Condition newCondition(); //返回绑定到此Lock实例的新 Condition 实例。
Lock接口中出现的Condition接口,它是什么作用呢?
Condition
wait()、notify()和synchronized配合可以实现等待通知,Condition和Lock配合同样也可以实现等待通知,但是两者之间还是有区别的
Condition定义了等待/通知两种类型的方法,当前线程调用这些方法,需要提前获取到Condition对象关联的锁,Condition对象是由Lock对象创建出来的(Lock.newCondition),换句话说,Condition是依赖Lock对象的。
ConditionAPI
public interface Condition {
void await() throws InterruptedException; //当前线程进入等待状态直到被通知(signal)或被中断
void awaitUninterruptibly(); //不响应中断等待,直到被通知(signal)
long awaitNanos(long nanosTimeout) throws InterruptedException; //等待指定时长直到被通知或中断或超时
boolean await(long time, TimeUnit unit) throws InterruptedException; //同上
boolean awaitUntil(Date deadline) throws InterruptedException; //当前线程进入等待状态直到被通知、中断或者到某个事件。若没有到指定事件就被通知,方法返回true,否则false。
void signal(); //唤醒一个等待在Condition上的线程,该线程从等待方法返回前必须获得与Condition相关联的锁。
void signalAll(); //唤醒所有等待在Condition上的线程,该线程从等待方法返回前必须获得与Condition相关的锁
}
ReentrantLock源码
构造器
public ReentrantLock() {sync = new NonfairSync();} //默认使用非公平锁对象
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();} //根据参数,判断使用 非公平/公平 锁。
内部类
构造器中,是创建内部类的对象NonfairSync(非公平锁)和FairSync(公平锁),它们都实现了Sync静态内部类
Sync静态内部类源码
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
abstract void lock();
//非公平锁获取锁
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread(); //当前线程
int c = getState(); //同步状态
if (c == 0) { //如果为0,当前锁未被其他线程获取
if (compareAndSetState(0, acquires)) { //通过CAS设置
setExclusiveOwnerThread(current); //设置获取锁的此线程
return true;
}
}
else if (current == getExclusiveOwnerThread()) { //如果锁状态不为0,则已经被其他线程获取。如果是当前线程,也就是同一个线程再次获取这个锁,即可重入
int nextc = c + acquires; //状态值增加。
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
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) { //状态值为0,释放锁
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
//检查锁是否为此线程的所有者。就是之前是否是这个线程获取的锁
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
//创建ConditionObject对象
final ConditionObject newCondition() {
return new ConditionObject();
}
//返回该锁的线程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
//返回锁的状态值。如果是该锁的线程,返回对应的值;不是返回0
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
//判断是否已被其他线程获取,是否已经锁住
final boolean isLocked() {
return getState() != 0;
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
NonfairSync非公平锁的源码实现
非公平锁就是一种获取锁的抢占机制,是 随机获得锁 的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁,结果也就是不公平的了.
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
//执行锁
final void lock() {
if (compareAndSetState(0, 1)) //通过CAS操作,设置状态值
setExclusiveOwnerThread(Thread.currentThread()); //设置该锁的线程
else
acquire(1);
}
//尝试获取锁。默认是非公平锁,转至Sync中的方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
FairSync公平锁的源码实现
公平锁表示线程获取锁的顺序是按照 线程加锁的顺序 来分配的,即先来先得的 FIFO 先进先出顺序。
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
//执行加锁
final void lock() {
acquire(1);
}
//尝试获取锁
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread(); //当前线程
int c = getState(); //状态值
if (c == 0) { //等于0,该锁未被线程获取
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { //hasQueuedPredecessors()方法判断头节点是否为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { //如果不等于0,判断该锁的线程是否为当前线程,是可重入。
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
方法
//获得锁
public void lock() {
sync.lock();
}
//当前线程未被中断,获取锁
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
//该锁未被其他线程获取,则当前线程获取锁。默认非公平锁
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
//给定时间内,该锁未被其他线程获取,该线程未被中断,可获取锁
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
//释放锁
public void unlock() {
sync.release(1);
}
//返回Condition对象
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;
}
//返回拥有该锁的线程
protected 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();
}
//查询是否有线程正在等待与此锁关联的给定条件。
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);
}
//返回与此锁关联的给定条件下等待的线程数的估计值。
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);
}
总结
- ReentrantLock 提供了内置锁(synchronized)类似的功能和内存语义。
- ReentrantLock 提供了包括定时的锁等待、可中断的锁等待、公平性以及实现非块结构的加锁。
- Condition 对线程的等待和唤醒等操作更加灵活,一个 ReentrantLock 可以有多个 Condition 实例,更有扩展性。
- ReentrantLock 需要显示的获取锁,并在 finally 中释放锁。
- JDK 1.8 以前 ReentrantLock 在性能上似乎优于 synchronized,但获取锁的操作不能与特定的栈帧关联起来,而内置锁可以。
- 因为内置锁时 JVM 的内置属性,所以未来更可能提升 synchronized 而不是 ReentrantLock 的性能。
- 例如对线程封闭的锁对象消除优化,通过增加锁粒度来消除内置锁的同步。