JUC锁框架——基于AQS的实现,从ReentrantLock认识独占和共享

馋奶兔 提交于 2019-11-27 02:35:00

JDK中有以下基于AQS的实现
ReentrantLock
CountDownLatch
Semaphore
ReentrantReadWriteLock
CyclicBarrier (委托给ReentrantLock)

首先关于源码中经常出现 final ReentrantLock takeLock = this.takeLock 写法:
这是一个有关volatile变量的lock-free的典型习惯编码。在第一行第一次到读到变量后,另一个线程会更新这个值,但你只对初始读到的值感兴趣。
另外,即使我们讨论的成员变量不是 volatile而是final, 这个习惯用法也与CPU缓存有关:从栈读变量比从堆读变量会更cache-friendly,本地变量最终绑定到CPU寄存器的可能性更高。

ReentrantLock的独占和共享:

基本上一致,区别:
非公平锁是:
先state+1,然后直接得到锁,
而公平锁则是:
先尝试去获取锁,如果得到了锁则state+1.
实现公平性的关键在于:如果锁被占用且当前线程不是持有者也不是等待队列的第一个,则进入等待队列。
可见是否公平实际上是对处于等待队列中的线程来说的。

ReentrantLock都是把具体实现委托给内部类
而不是直接继承自AbstractQueuedSynchronizer,
这样的好处是用户不会看到不需要的方法,
也避免了用户错误地使用AbstractQueuedSynchronizer的公开方法而导致错误。

    // 非公平获取  
    final boolean nonfairTryAcquire(int acquires) {  
        final Thread current = Thread.currentThread();  
        int c = getState();  
        if (c == 0) {  
      // 锁是空闲的,进行加锁必须用CAS来确保即使有多个线程竞争锁也是安全的  
            if (compareAndSetState(0, acquires)) {  
                // 加锁成功  
                // 把当前线程设为锁的持有者,在获取前可用于判断是否是重入。  
                setExclusiveOwnerThread(current);  
                return true ;  
            }  
        }  
        else if (current == getExclusiveOwnerThread()) {  
             // 锁被占用且当前线程是锁的持有者,说明是重入。  
            int nextc = c + acquires;  
            if (nextc < 0)  
                   // 溢出。加锁次数从0开始,加锁与释放操作是对称的,  
                   // 所以绝不会是小于0值,小于0只能是溢出。  
                throw new Error("Maximum lock count exceeded");  
            // 锁被持有的情况下,只有持有者才能更新锁保护的资源,  
            // 所以这里不需要用CAS。  
            setState(nextc);  
            return true ;  
        }  
        return false ;  
    }  
static final class FairSync extends Sync {  
    private static final long serialVersionUID = -3000897897090466540L;  
  
    final void lock() {  
        acquire(1); //acquire会首先调用tryAcquire,所以公平策略的控制留给tryAcquire。  
    } 
    // tryAcquire的公平版本。除非是递归调用或没有等待者或者是第一个,否则不授予访问。  
    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 ;  
    }  
}  
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!