重入锁的实现原理
内建锁隐式的支持重入性,synchronized通过获取自增,释放自减的方式实现重入.
重入锁的特性:
- 线程获取锁时,如果已经获取锁的线程是当前线程直接获取.
- 因为可以获取N次,所以只有当释放N次后才算真正释放成功.
非公平锁nonfairTryAcquire():
final boolean nonfairTryAcquire(int acquires) {
//拿到当前线程
Thread current = Thread.currentThread();
//获取当前同步状态
int c = getState();
if (c == 0) {
//当前同步状态还未被获取,当前线程使用CAS尝试获取同步状态
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//此时同步状态不为0,表示已经有线程获取到了当前状态,
//判断持有线程是否是当前线程
else if (current == getExclusiveOwnerThread()) {
//若是当前线程,同步状态再次+1
int nextc = c + acquires;
if (nextc < 0) {
throw new Error("Maximum lock count exceeded");
}
//将再次+1后的状态写回内存
setState(nextc);
return true;
}
return false;
}
公平锁的tryAcquire():
protected final boolean tryAcquire(int acquires){
final Thread current = Thread.currentThread();
int c = getState();
if(c == 0){
//当同步队列中存在非空节点,当前线程直接封装为Node节点排队
if(!hasQueuedPredecessors() && comparaAndSetState(0,1))
setExclusiveOwnerThrad(current);
return true;
}
else if(current == getExclusiveOwnerThread()){
int nxetc = c + acquires;
if(nextc < 0){
throw new Error("Maximum lock count exceeded");
}
setState(nextc);
return true;
}
return false;
}
对比非公平锁的nonfairAcquire和公平锁的tryAcquire,可以发现公平锁就是因为比公平锁多了一个!hasQueuedPredecessors()条件,即
if(!hasQueuedPredecessors() && comparaAndSetState(0,1)).也就是当发现同步队列不为空时,直接将当前线程封装为Node节点后入队.这样实现的公平性.
非公平锁nonFairSync
final void lock(){
//直接CAS尝试获取锁
if(compareAndSetState(0,1))
setExclusiveOwnerThread(Thread.currentThread());
else{
acquire(1);
}
}
公平锁FairSync
final void lock(){
//少了一次CAS过程
acquire(1);
}
对比非公平锁nonFairSync和公平锁FairSync,可以发现非公平锁会先CAS尝试获取锁,获取不成功再执行acquire(),而公平锁没有进行CAS.
比较:公平锁保证了第一个获取锁的均为同步队列中的第一个节点,保证了请求资源时间上的绝对顺序,但是效率低下,需要进行频繁的上下文切换.非公平锁会降低性能开销,降低一定的上下文切换,但是可能导致其他线程永远换取不到锁,造成线程的饥饿现象.
ReentrantReadWriteLock
读写锁允许同一时刻被多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均会被阻塞.
写锁的获取-tryAcquire()
protected final boolean tryAcquire(int acquires){
Thread current = Thread.currentThread();
//获取写读写锁状态
int c = getState();
//获取独占式锁状态 -即写锁状态
int w = exclusive(c);
if(c != 0){
//表明当前有读线程拿到读锁,写线程无法获取同步状态
if(w == 0 || current != getExclusiveOwnerThread()){
return false;
}
if(w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maxinum lock count exceeded");
//没有达到最大值,写锁可重入,再次加一即可
setState(c + acquires);
return true;
}
if(writerShouldBlock || !compareAndSetState(c,c+acquires))
return false;
//此时读写状态为0,写锁可以获取到同步状态,将当前写锁置为只有写锁线程
setExclusiveOwnerThread(current);
return true;
}
写锁的释放-tryRelease
protected final boolean tryRelease(int releases){
if(!HeldExclusively()){
throw new IllegalMoniterStateException();
}
//同步状态减去写状态
int nextc = getState() - releases;
//当前写状态是否为0,为0则释放写锁
boolean free = exclusiveCount(nextc) == 0;
if(free){
//将独占式锁的线程置空
setExclusiveOwnerThread(null);
//不为0则更新同步状态
setState(nextc);
return free;
}
}
读锁只要当前没有写线程获取到写锁,并且读线程的获取没有达到最大值,读锁就能成功.
来源:CSDN
作者:我的程序才没有bug!
链接:https://blog.csdn.net/qq_41299382/article/details/103595722