ReentrantReadWriteLock笔记(三) -- WriteLock源码分析

先看一下ReentrantReadWriteLock的继承图

ReentrantReadWriteLock

ReadWriteLock接口很简单,就两个方法,就是返回读锁、写锁的方法

public interface ReadWriteLock {
    Lock readLock();
    Lock writeLock();
}

可以发现它没有继承Lock接口,再想想平常使用是lock.writeLock().lock()这样子的,所以接着往下看ReentrantReadWriteLock怎么实现writeLock方法的

public ReentrantReadWriteLock.WriteLock writeLock() { 
    return writerLock; 
}
//writerLock是ReentrantReadWriteLock类的一个成员变量
private final ReentrantReadWriteLock.WriteLock writerLock;  
//它在构造方法里被初始化
public ReentrantReadWriteLock(boolean fair) {
   sync = fair ? new FairSync() : new NonfairSync();
    readerLock = new ReadLock(this);
    writerLock = new WriteLock(this);
}

//而WriteLock的构造方法是
protected WriteLock(ReentrantReadWriteLock lock) {
   sync = lock.sync;
}

WriteLock是ReentrantReadWriteLock的一个内部类,和ReentrantLock很相似,锁的相关方法交给了ReentrantReadWriteLock的内部类Sync来代理

WriteLock

可以看到WriteLock实现了Lock接口,所以有lock()、unlock()等方法,下面就开始分析其实现逻辑,由于有公平锁、非公平锁两种,那就先分析非公平锁,从lock()方法入手

PS: 由于和ReentrantLock差不多,一些地方就不多解析,相关文章:ReentrantLock笔记(二) – 公平/非公平锁源码分析

    public void lock() {
        sync.acquire(1);
    }
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();//设置中断标志
    }

看到tryAcquire()方法又是要看具体Sync类怎么实现

protected final boolean tryAcquire(int acquires) {
     Thread current = Thread.currentThread();
     int c = getState();
     int w = exclusiveCount(c);//获取已经分配了的排它资源数
     if (c != 0) {//不为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;
 }

看看writerShouldBlock是啥?

abstract boolean writerShouldBlock();

是个抽象方法,看非公平锁(NonfairSync)如何实现

final boolean writerShouldBlock() {
    return false;
}

直接返回了false,那就继续看CAS设置状态compareAndSetState(c, c + acquires)是否成功了,不成功就说明被其他线程抢先获得了资源,所以就返回false。

回顾一下上面 if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) , tryAcquire获取失败那就要看acquireQueued方法了。

该方法在前面的文章ReentrantLock笔记(二) – 公平/非公平锁源码分析有分析过,所以大致说一下:该方法是把当前线程加入sync队列,并开始等待自己排到队列的第二个节点,就会开始继续尝试tryAcquire,成功就会获得资源,成为队列头节点。

总结一下流程:

  • 步骤A:线程请求写锁,先判断当前分配资源数是否大于0
  • 步骤B:大于0说明有线程获得了资源,就进行排它资源数判断,如果等于0,那就说明分配的资源是共享资源(读锁),那就不能获得写锁了;如果不等于0,那就要判断之前是不是当前线程获得了排它资源,不是的话就失败
  • 步骤C:当步骤A中判断分配的资源数不是大于0,那就是等于0,说明现在没有线程获得资源(锁),直接进行CAS操作,成功就是获得资源(锁),失败就返回false表示失败
  • 步骤D:步骤B、步骤C都失败的话,就要进入sync队列,并开始休眠,等待自己排到队列的第二个节点,就会开始继续尝试tryAcquire,成功就会获得资源,成为队列头节点,失败就继续休眠。

现在来看公平锁(FairSync)如何实现,主要看writerShouldBlock()这个抽象方法它怎么实现

final boolean writerShouldBlock() {
    return hasQueuedPredecessors();
}

hasQueuedPredecessors()是在当前线程排在了sync队列中的第二个节点时会返回false, 回顾下前面if(writerShouldBlock() ||!compareAndSetState(c, c + acquires)), writerShouldBlock()返回false才会进行后面的CAS操作,这样就保证了下一次获得资源(锁)的线程是按队列出队的顺序获得的,所以是公平的


来看看unlock方法,公平锁和非公平锁都是一样的,比较简单

public void unlock() {
    sync.release(1);
}

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);//唤醒后置节点
        return true;
    }
    return false;
}

protected final boolean tryRelease(int releases) {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    int nextc = getState() - releases;
    boolean free = exclusiveCount(nextc) == 0;
    if (free)
        setExclusiveOwnerThread(null);
    setState(nextc);
    return free;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值