ReenrantLock源码剖析

坚强是说给别人听的谎言 提交于 2019-12-19 00:33:31

重入锁的实现原理

内建锁隐式的支持重入性,synchronized通过获取自增,释放自减的方式实现重入.

重入锁的特性:
  1. 线程获取锁时,如果已经获取锁的线程是当前线程直接获取.
  2. 因为可以获取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;
       }
}

读锁只要当前没有写线程获取到写锁,并且读线程的获取没有达到最大值,读锁就能成功.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!