ReentrantLock(可重入锁)
ReentrantLock获取锁和释放锁可以参考这篇博客:https://blog.csdn.net/sophia__yu/article/details/84313234
一:ReentrantLock如何实现可重入
ReentrantLock实现Lock接口的可重入子类。可重入的意思就是一个线程在获取锁的时候,如果该线程已经获取到锁则直接获取成功,不会被阻塞;
由于会获取n次锁,那么在锁释放的时候也需要释放n次,才能完全释放成功。
可重入锁是在tryAcquire()阶段实现的:
源码如下:
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()) {
//如果是,再次获取将状态+1,实现可重入
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
如果该同步状态不为0,表示此时同步状态已被线程获取,再判断同步状态的线程是否是当前线程,如果是,同步状态再次加1,并返回true,表示持有线程重入同步块。
可重入锁释放
源码如下:
protected final boolean tryRelease(int releases) {
//释放锁第一步,将同步状态-1
int c = getState() - releases;
//如果持有锁线程不是当前线程直接抛异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//如果同步状态为0,才将当前持有锁线程置空
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
//如果同步状态不为0,更新同步状态,锁没有完全释放
setState(c);
return free;
}
当且仅当同步状态减为0并且持有线程为当前线程时表示正确释放。
否则调用setState()将-1后的状态进行更新。
二:公平锁与非公平锁
synchronized可以实现重入,但是synchronized只能实现非公平锁,而
ReentrantLock即可以实现公平锁也可以实现非公平锁。 那什么是公平锁和非公平锁呢?
如果线程获取锁的顺序与请求等待时间保持一致,满足FIFO,那么这个锁是公平的,否则为非公平锁。
看一系列ReentrantLock源码:
public ReentrantLock() {
//非公平锁
sync = new NonfairSync();
}
可以看到ReentrantLock锁默认是非公平锁,但是有一个重载方法实现公平机制:
public ReentrantLock(boolean fair) {
//如果fair是true,是实现公平,否则非公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
要使用公平锁,调用ReentrantLock有参构造传入true,获取内置的公平锁。
非公平锁与公平锁实现区别:
非公平锁:
static final class NonfairSync extends Sync {
final void lock() {
//首先先CAS,新启动线程很有可能不入队,直接获取同步状态,那么队头线程依然在等待
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
/再次尝试获取锁
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//如果同步状态为0,直接CAS,那么新的线程可能会抢占已经排队的线程的锁的使用权,也就是队头线程依然在等待
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else i