重入性:表示能够对共享资源能够重复加锁,即当前线程获取该锁再次获取不会被阻塞。
重入性的实现原理
判断当前线程能否获得锁为例,核心方法为
nonfairTryAcquire():
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)
throw new Error("Maximum lock count exceeded");
setState(nextc);//更新锁的状态
return true;
}
return false;
}
释放锁的核心方法是tryRelease():
protected final boolean tryRelease(int releases) {
// 同步状态-1
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();//非持有锁线程调用tryRelease()方法抛出此异常
boolean free = false;
if (c == 0) {
// 只有当同步状态为0时,锁成功释放,返回false
free = true;
setExclusiveOwnerThread(null);
}
// 锁未被完全释放,返回fal
setState(c);
return free;
}
公平锁与非公平锁
公平锁:锁的获取顺序符合请求上的绝对时间顺序,满足FIFO(先进先出)。
ReentrantLock的构造方法无参时是构造非公平锁,源
码为:
public ReentrantLock() {
//默认为非公平锁
sync = new NonfairSync();
}
另外还提供了另外一种方式,可传入一个boolean值,true时为公平锁,false时为非公平锁,源码为:
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
如下图,观察公平锁与非公平锁的区别,公平锁获取同步状态之前先判断当前结点是否有前驱结点,如果有就获取失败,实现了先进先出的规则。
公平锁和非公平锁的比较
1.公平锁每次获取到锁为同步队列中的第一个节点,保证请求资源时间上的绝对顺序,而非公平锁有可能刚释放锁的线程下次继续获取该锁,则有可能导致其他线程永远无法获取到锁,造成“饥饿”现象。
2. 公平锁为了保证时间上的绝对顺序,需要频繁的上下文切换,效率低。而非公平锁会降低一定的上下文切换,降低性能开销效率高。
3. ReentrantLock默认选择的是非公平锁,则是为了减少一部分上下文切换,保证了系统更大的吞吐量。
深入理解读写锁ReentrantReadWriteLock
读写锁:允许同一时刻被多个读线程访问,但是在写线程访问时,所有的读线程和其他的写线程都会被阻塞。
读锁==无锁?
肯定不等于。因为在写线程访问的时候,所有的读线程不能再访问,被阻塞。
写锁
写锁的获取
然写锁是独占式锁,而实现写锁的同步语义是通过重写AQS中的tryAcquire方法实现的。源码为:
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
//判断当前同步状态
int c = getState();
//获取写锁获取的次数
int w = exclusiveCount(c);
if (c != 0) {
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
setState(c + acquires);
return true;
}
//写锁未被任何线程获取,当前线程获取写锁
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
//获取写锁成功
setExclusiveOwnerThread(current);
return true;
}
exclusiveCount©方法,我们怎么直到他获取的是读写还是写锁呢?看该方法源码:
其 中 EXCLUSIVE_MASK 为 : static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; EXCLUSIVE_MASK为1左移16位然后减1,即为0x0000FFFF。而exclusiveCount方法是将同步状态(state为int类型)与0x0000FFFF相与,即取同步状态的低16位。那么低16位代表什么呢?根据exclusiveCount方法的注释为独占式获取的次数即写锁被获取的次数,现在就可以得出来一个结论:同步状态的低16位用来表示写锁的获取次数。
再看上图中的sharedCount()方法。该方法是获取读锁被获取的次数,是将同步状态(int c)右移16次,即取同步状态的高16位,现在我们可以得出另外一个结论同步状态的高16位用来表示读锁被获取的次数。
写锁的释放
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 同步状态减去写状态
int nextc = getState() - releases;
// 当前写状态是否为0,为0则释放
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
// 不为0则更新同步
setState(nextc);
return free;
}
读锁
读锁的获取
读锁不是独占式锁,即同一时刻该锁可以被多个读线程获取也就是一种共享式锁。
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
// 如果写锁已经被获取并且获取写锁的线程不是当前线程
// 线程获取读锁失败并返回
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
// 当前线程获取
compareAndSetState(c, c + SHARED_UNIT)) {
// 新增关于读锁的一些功能,比如getReadHoldCount()方法返回
// 当前获取读锁的
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
// CAS失败或者已经获取读锁的线程再次重入
return fullTryAcquireShared(current);
}
读锁的释放
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
// Releasing the read lock has no effect on readers,
// but it may allow waiting writers to proceed if
// both read and write locks are now free.
return nextc == 0;
}
}
锁降级:遵循按照获取写锁,获取读锁再释放写锁的次序,写锁能够降级成为读锁,不支持锁升级