共计 2557 个字符,预计需要花费 7 分钟才能阅读完成。
上一文写了独占锁与共享锁,这次再聊聊重入锁。
重入是指任意线程在获取到锁之后能再次获取该锁而不会被锁阻塞,基于这个定义会引出两个问题:
- 线程再次获取锁。锁须要去辨认获取锁的线程是否为以后占据锁的线程,如果是则再次获取胜利。这里会波及到锁获取的偏心与非偏心,上面会提到。
- 锁的最终开释。线程反复 N 次获取锁,随后在第 N 次开释锁后其它线程可能获取到锁。
咱们晓得 synchronized 是反对隐式的重入的,在 synchronized 润饰的办法如果呈现递归,线程并不会阻塞本人。ReentrantLock 是通过组合自定义同步器来实现锁的获取与开释,以非公平性(默认)实现。
重要的外部类
ReentrantLock 里有 3 个重要的外部类,别离是 Sync、NonfairSync、FairSync:
- Sync 是前面两个的父类,继承至 AbstractQueuedSynchronizer。
- NonfairSync 和 FairSync 都继承至 Sync。
- NonfairSync 次要用于实现非偏心锁,FairSync 次要用于实现偏心锁。
重要的属性 private final Sync sync;
在构造方法中初始化,通过构造方法参数决定应用偏心锁还是非偏心锁实现。
重要的结构器
// 不偏心锁
public ReentrantLock() {sync = new NonfairSync();
}
// 偏心锁
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}
偏心与非偏心锁的区别
偏心与是针对获取锁而言的,如果一个锁是偏心的,那么锁的获取程序就应该合乎申请的相对工夫程序,也就是 FIFO。
上面咱们来比拟下偏心锁与非偏心锁获取的代码:
static final class NonfairSync extends Sync {
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);
}
}
// 获取非偏心锁
final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();
// 同一线程获取锁的次数
// 如果同一线程未开释且再次获取胜利会累加次数
int c = getState();
if (c == 0) {// 还没有线程获取锁
if (compareAndSetState(0, acquires)) {
// 将以后线程设置为独占锁的线程
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);
return true;// 获取胜利
}
return false;
}
// 偏心获取锁
static final class FairSync extends Sync {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;
}
}
接着看下锁的开释:
protected final boolean tryRelease(int releases) {int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
如果该锁被获取了 N 次,那么前 (N-1) 次开释时会返回 false,只有同步状态齐全开释了才会返回 true。
偏心锁与非偏心锁总结
- 偏心锁在获取过程中会判断退出同步队列中的以后节点是否有前驱节点,这是与非偏心锁获取惟一不同点。
- 偏心锁遵循 FIFO,能够防止极饿。
- 非偏心锁能够缩小线程上下文切换,吞吐量较偏心锁高。
参考文章:①ReentrantLock 之偏心锁和非偏心锁
正文完