• ReentrantReadWriteLock


    ReentrantReadWriteLock 这个对象,有两个内部类,readLock和writeLock,都有一个aqs的属性sync,实例化的时候,获取的是从ReentrantReadWriteLock自己的重写的内部类sync继承了aqs

     

    看readLock的lock()方法

    RWlock 的 内部类sync 继承了aqs之后,对state进行了设计,后16位用来表示exlusiveCount,排他的数目。前16位用来表示sharedCount,共享的数目。readLock 的 Lock 调用的是 sync.acquireShared(1),这是aqs的方法,

    if (tryAcquireShared(arg) < 0)

    doAcquireShared(arg);

    其中,tryAcquireShared是RWLock自己重写了的

    用意是:试着获取共享锁,如果获取成功,返回的是正数,如果获取失败,返回的是-1,

    逻辑是:

    如果exlusiveCount不为0,说明此时有人使用排它锁,而且排它锁的线程不是当前线程,所以不能重入,那么现在的试图获取共享锁的操作是失败的

    如果以上不成立,那么,说明可以获取共享锁,如果用cas添加sharedcount成功,那么返回1

    否则,进入full啥啥啥方法,逻辑和本方法差不多,只不过在for循环中,为了弥补cas miss,其实是个延迟加载

     

    看readLock的unlock()方法

    public  void unlock() {
                sync.releaseShared(1);
            }
    public final boolean releaseShared(int arg) {
            if (tryReleaseShared(arg)) {
                doReleaseShared();
                return true;
            }
            return false;
        }

    其中,tryReleaseShared是RWLOCK重写的,tryReleaseShared的逻辑是,获取sharedstate,减一,返回剩下的sharedstate是否等于0,如果不为0,不执行doReleaseShared方法,否则执行

    doReleaseShared是AQS的,逻辑是很熟悉的:将head节点的后继节点唤醒。现在是readlock获取到锁之后的unlock,那么挂起线程的列表中绝不会有readlock的申请者,因为是共享的,所以试图获取读锁的线程不会被挂起。所以挂起的线程都是

    写锁,那么现在读锁全部释放,唤醒写锁线程也是非常合理的。 

     

    看WriteLock的lock()方法

     

     public void lock() {
                sync.acquire(1);
            }
    public final void acquire(int arg) {
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }

     

    其实还是正常的获取锁的逻辑:尝试获取,如果获取到了,就设置state,exlusiveThread等待,然后往下走,如果获取不到就把线程挂起到队列里

    关键是尝试获取的逻辑不同,交给不同的子类sync重写

    RWLOCK的tryAcquire的逻辑是:

    protected final boolean tryAcquire(int acquires) {
          Thread current = Thread.currentThread();
                int c = getState();
                int w = exclusiveCount(c);
            
    if (c != 0) { // (Note: if c != 0 and w == 0 then shared count != 0) if (w == 0 || current != getExclusiveOwnerThread()) return false; if (w + exclusiveCount(acquires) > MAX_COUNT) throw new Error("Maximum lock count exceeded"); // Reentrant acquire setState(c + acquires); return true; } if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) return false; setExclusiveOwnerThread(current); return true; }

    先不看具体逻辑,试想一个,如果一个写锁可以获取,那么前提应该是,没有读锁,没有任何别的写锁,也就是如果有写锁,只能是自己,这也是c != 0中的第一个条件判断的,其他的判断都是判断有没有其他写锁了,这个和普通的独占锁是一样的。

     

     

    看WriteLock的unlock()方法

     

     

    public final boolean release(int arg) {
            if (tryRelease(arg)) {
                Node h = head;
                if (h != null && h.waitStatus != 0)
                    unparkSuccessor(h);
                return true;
            }
            return false;
        }

     

    还是一样的逻辑,try方法自己重写,其他方法继承,

    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;
            }

    逻辑也很简单,减去state上的数目,因为exlusive的是后16位,所以直接减就行了,然后看看后16位是不是0,如果是的话返回真,不是的返回假。返回真的话,就可以唤醒挂起的线程,这个时候的挂起线程就是等待的写线程。

  • 相关阅读:
    VS开发工具 因插件问题导致 已停止工作 解决办法
    niceScroll 简单使用 及 插件API
    使用JQGrid 问题汇总 不定时更新
    H5特性 MutationObserver 监听元素 动态改变iframe高度
    UVALIVE 3972 March of the Penguins
    Codeforces #105 DIV2 ABCDE
    UVALIVE 3644 X-Plosives
    UVALIVE 3645 Objective: Berlin
    UVALIVE 3031 Cable TV Network
    UVALIVE 2927 "Shortest" pair of paths
  • 原文地址:https://www.cnblogs.com/chuliang/p/8125316.html
Copyright © 2020-2023  润新知