JUC源码分析13-locks-ReentrantReadWriteLock

来源:互联网 发布:appstore付费软件 编辑:程序博客网 时间:2024/04/30 11:35

ReentrantReadWriteLock基于AQS实现读写锁的同步:

1.利用共享模式实现读锁,独占模式实现写锁;

2.支持公平和非公平,非公平的情况下可能会出现读锁阻塞写锁的场景;

3.写锁阻塞写锁和读锁,读锁阻塞写锁;

4.写锁可以降级为读锁,读锁不能升级为写锁,只能先release再lock;

5.写锁支持condition条件;

6.读写锁都支持超时/中断lock;

7.适合读多写少的场景。


实现ReadWriteLock接口,用于返回读/写锁:

<span style="font-size:18px;">public interface ReadWriteLock {    /**     * Returns the lock used for reading.     */    Lock readLock();    /**     * Returns the lock used for writing.     */    Lock writeLock();}</span>

看下内部类的AQS实现:

<span style="font-size:18px;">abstract static class Sync extends AbstractQueuedSynchronizer {    private static final long serialVersionUID = 6317671515068378041L;    /*AQS中的int型state字段被拆为2部分,高16位表示共享读锁的持有次数(每个线程的重入次数,由HoldCounter保存),低16位表示独占写锁的重入次数     */    static final int SHARED_SHIFT   = 16; //偏移单位    static final int SHARED_UNIT    = (1 << SHARED_SHIFT); //00000000 00000001 00000000 00000000 state拆为2部分,所以读锁的持有次数计算都需要这个值做比较    static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1; //00000000 00000000 11111111 11111111 读写锁的最大持有次数65535,2的16次方-1    static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; //00000000 00000000 11111111 11111111    /** 读锁高16位无符号偏移16位,相当于计算读锁的持有持有次数  */    static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }    /** 返回写锁的重入次数,state2种情况:如果拥有读锁,肯定大于65535,就用到了高16位,做&操作的话就等于0,可以用state!=0加这个返回值!=0判断拥有读锁如果是写锁的话,肯定是小于65535,用到了低16位,做&操作就返回写锁的重入次数*/    static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }    /** 定义类保存读锁每个线程重入次数 */    static final class HoldCounter {        int count = 0;        // 用id,而不是用thread保存,编译垃圾滞留        final long tid = Thread.currentThread().getId();    }    /** ThreadLocal子类,持有HoldCounter*/    static final class ThreadLocalHoldCounter        extends ThreadLocal<HoldCounter> {        public HoldCounter initialValue() {            return new HoldCounter();        }    }    /** 读锁的重入次数变量,在内部类Sync构造时初始化,在读锁release的重入减少到1时remove,然后-- */    private transient ThreadLocalHoldCounter readHolds;    /** 缓存最后一个成功获取读锁的持有,javadoc解释是,下一个要release的就是最后一个成功获取的,也是为了处理优化  */    private transient HoldCounter cachedHoldCounter;    /** 为了处理优化,保存第一个进来的线程和重入次数  */    private transient Thread firstReader = null;    private transient int firstReaderHoldCount;    Sync() {        readHolds = new ThreadLocalHoldCounter();//读锁的重入次数初始化        setState(getState()); // cas操作,加内存屏障,保证readHolds的可见性    }    /* 读写锁Acquire时候判断是否需要阻塞,公平和不公平实现处理方式不一样 */    abstract boolean readerShouldBlock();    abstract boolean writerShouldBlock();    /* AQS独占api写锁的release */    protected final boolean tryRelease(int releases) {//判断是否当前线程        if (!isHeldExclusively())            throw new IllegalMonitorStateException();        int nextc = getState() - releases;//写锁的重入次数判断        boolean free = exclusiveCount(nextc) == 0;        if (free)            setExclusiveOwnerThread(null); //写锁重入为0时true,设置独占线程null        setState(nextc);        return free;    }/* AQS独占api写锁的acquire */    protected final boolean tryAcquire(int acquires) {        /*         * Walkthrough:         * 1. If read count nonzero or write count nonzero         *    and owner is a different thread, fail. 如果读锁或写锁不为0,且占有线程不是当前线程,false         * 2. If count would saturate, fail. (This can only         *    happen if count is already nonzero.) 持有次数大于最大65535,false         * 3. Otherwise, this thread is eligible for lock if         *    it is either a reentrant acquire or         *    queue policy allows it. If so, update state         *    and set owner. 否则,如果是重入的或者按照队列策略(应该是可以插队的情况下)容许,那就更新state值设置owner线程         */        Thread current = Thread.currentThread();        int c = getState();        int w = exclusiveCount(c); //上面说过2种情况:1.返回写锁的重入次数;2.返回0,可用于判断是否有读锁        if (c != 0) {//c!=0 表示锁被占用            // c!=0 and w==0表示用读锁,这样的话,读锁是阻塞写锁的返回false,挂起// c!=0 and w!=0表示有写锁,就判断下是不是重入,不是false,挂起            if (w == 0 || current != getExclusiveOwnerThread())                return false;            if (w + exclusiveCount(acquires) > MAX_COUNT) //判断下是不是达到了最大重入次数                throw new Error("Maximum lock count exceeded");            // 到这里的话,那就当前线程重入了,那就设置state值,返回true,Acquire成功            setState(c + acquires);            return true;        }//到这里那就是c为0了,需要看看是不是需要挂起(由公平和和非公平子类实现)//非公平直接返回false,公平的话就检查hasQueuedPredecessors检查head的next是不是非当前线程        if (writerShouldBlock() ||            !compareAndSetState(c, c + acquires))            return false; //需要挂起或cas失败,那就挂起吧        setExclusiveOwnerThread(current);        return true;    }/*AQS共享api读锁release实现*/    protected final boolean tryReleaseShared(int unused) {        Thread current = Thread.currentThread();        if (firstReader == current) {            //判断缓存的重入,如果只有一次,那就直接设置缓存线程null,否则递减            if (firstReaderHoldCount == 1)                firstReader = null;            else                firstReaderHoldCount--;        } else {//从缓存的读锁重入变量里面取            HoldCounter rh = cachedHoldCounter;            if (rh == null || rh.tid != current.getId())                rh = readHolds.get();            int count = rh.count;            if (count <= 1) {                readHolds.remove();                if (count <= 0)                    throw unmatchedUnlockException();            }            --rh.count; //递减重入次数        }        for (;;) { //for循环loop设置读锁的holdCount减少            int c = getState();            int nextc = c - SHARED_UNIT;            if (compareAndSetState(c, nextc))                // Releasing the read lock has no effect on readers,释放读锁对其他读线程没有什么影响                // but it may allow waiting writers to proceed if                // both read and write locks are now free.// 如果读锁和写锁都空闲,就可以容许其他写线程处理,// 但是如果读多写少的场景下,非公平模式,很可能读释放了,写线程也没机会                return nextc == 0;        }    }    private IllegalMonitorStateException unmatchedUnlockException() {        return new IllegalMonitorStateException(            "attempt to unlock read lock, not locked by current thread");    }/*AQS共享api读锁Acquire实现*/    protected final int tryAcquireShared(int unused) {        /*         * Walkthrough:         * 1. If write lock held by another thread, fail. 如果其他线程获取了写锁,false,也就是写锁阻塞了读锁         * 2. Otherwise, this thread is eligible for         *    lock wrt state, so ask if it should block         *    because of queue policy. If not, try         *    to grant by CASing state and updating count.         *    Note that step does not check for reentrant         *    acquires, which is postponed to full version         *    to avoid having to check hold count in         *    the more typical non-reentrant case. 否则,当前线程获取了写锁,根据队列策略看是否要阻塞读锁,不阻塞那就setstate,更新读锁重入次数          * 3. If step 2 fails either because thread         *    apparently not eligible or CAS fails or count         *    saturated, chain to version with full retry loop. 如果第二步失败了那就fullTryAcquireShared         */        Thread current = Thread.currentThread();        int c = getState();        if (exclusiveCount(c) != 0 &&            getExclusiveOwnerThread() != current) //有写锁并且不是当前线程,挂起            return -1;        int r = sharedCount(c); //读锁的holdCount        if (!readerShouldBlock() && //公平非公平子类决定读锁是否阻塞            r < MAX_COUNT &&             compareAndSetState(c, c + SHARED_UNIT)) //cas设置state,注意updae值加了65535,保证更新的值是高16位{            if (r == 0) {//读锁只有一个,直接缓存,不用放到readHolds里面                firstReader = current;                firstReaderHoldCount = 1;            } else if (firstReader == current) {//不为0,但是缓存的是当前线程,直接累加                firstReaderHoldCount++;            } else {//其他情况,那就只能从缓存变量取值更新了                HoldCounter rh = cachedHoldCounter;                if (rh == null || rh.tid != current.getId())                    cachedHoldCounter = rh = readHolds.get();                else if (rh.count == 0)                    readHolds.set(rh);                rh.count++;            }            return 1;        }//需要阻塞、读锁持有超过最大、cas失败那就for循环重试        return fullTryAcquireShared(current);    }    /** 完全Acquire判断处理cas失败或者读锁重入  */    final int fullTryAcquireShared(Thread current) {        /*         * This code is in part redundant with that in         * tryAcquireShared but is simpler overall by not         * complicating tryAcquireShared with interactions between         * retries and lazily reading hold counts.         */        HoldCounter rh = null;        for (;;) {            int c = getState();            if (exclusiveCount(c) != 0) {                if (getExclusiveOwnerThread() != current)//到这里的话,其他线程持有写锁                    return -1;                // else we hold the exclusive lock; blocking here                // would cause deadlock.//否则当前线程持有写锁,阻塞在这里会造成死锁            } else if (readerShouldBlock()) {//写锁空闲,并且读锁需要阻塞                // Make sure we're not acquiring read lock reentrantly                if (firstReader == current) {//如果是当前线程的话,即使需要队列策略决定需要阻塞也不阻塞,直接后面cas操作                    // assert firstReaderHoldCount > 0;                } else {                    if (rh == null) {                        rh = cachedHoldCounter;                        if (rh == null || rh.tid != current.getId()) {                            rh = readHolds.get();                            if (rh.count == 0)                                readHolds.remove();                        }                    }//需要阻塞且count==0为非重入的话,那就阻塞                    if (rh.count == 0)                        return -1;                }            }            if (sharedCount(c) == MAX_COUNT) //读锁持有超过最大                throw new Error("Maximum lock count exceeded");//下面的cas操作跟对应的处理和前面tryAcquireshard里面一样            if (compareAndSetState(c, c + SHARED_UNIT)) {                if (sharedCount(c) == 0) {                    firstReader = current;                    firstReaderHoldCount = 1;                } else if (firstReader == current) {                    firstReaderHoldCount++;                } else {                    if (rh == null)                        rh = cachedHoldCounter;                    if (rh == null || rh.tid != current.getId())                        rh = readHolds.get();                    else if (rh.count == 0)                        readHolds.set(rh);                    rh.count++;                    cachedHoldCounter = rh; // cache for release                }                return 1;            }        }    }    /** 写锁和tryAcquire相比少调用了writerShouldBlock.导致写锁的插队,不管你公平还是不公平了   */    final boolean tryWriteLock() {        Thread current = Thread.currentThread();        int c = getState();        if (c != 0) {            int w = exclusiveCount(c);            if (w == 0 || current != getExclusiveOwnerThread())                return false;            if (w == MAX_COUNT)                throw new Error("Maximum lock count exceeded");        }        if (!compareAndSetState(c, c + 1))            return false;        setExclusiveOwnerThread(current);        return true;    }    /** 读锁比fullreaderShouldBlock少判断了readerShouldBlock.也是读锁的插队,不管公平还是不公平模式了   */    final boolean tryReadLock() {        Thread current = Thread.currentThread();        for (;;) {            int c = getState();            if (exclusiveCount(c) != 0 &&                getExclusiveOwnerThread() != current)                return false;            int r = sharedCount(c);            if (r == MAX_COUNT)                throw new Error("Maximum lock count exceeded");            if (compareAndSetState(c, c + SHARED_UNIT)) {                if (r == 0) {                    firstReader = current;                    firstReaderHoldCount = 1;                } else if (firstReader == current) {                    firstReaderHoldCount++;                } else {                    HoldCounter rh = cachedHoldCounter;                    if (rh == null || rh.tid != current.getId())                        cachedHoldCounter = rh = readHolds.get();                    else if (rh.count == 0)                        readHolds.set(rh);                    rh.count++;                }                return true;            }        }    }// 当前线程是否是独占线程    protected final boolean isHeldExclusively() {        return getExclusiveOwnerThread() == Thread.currentThread();    }//写锁的condition    final ConditionObject newCondition() {        return new ConditionObject();    }// 获取独占线程    final Thread getOwner() {        // Must read state before owner to ensure memory consistency        return ((exclusiveCount(getState()) == 0) ?                null :                getExclusiveOwnerThread());    }//获取读锁持有次数    final int getReadLockCount() {        return sharedCount(getState());    }//是否写锁持有    final boolean isWriteLocked() {        return exclusiveCount(getState()) != 0;    }//如果当前线程为独占线程,获取下重入次数,否则0    final int getWriteHoldCount() {        return isHeldExclusively() ? exclusiveCount(getState()) : 0;    }//获取当前线程的重入次数    final int getReadHoldCount() {        if (getReadLockCount() == 0)            return 0;        Thread current = Thread.currentThread();        if (firstReader == current)            return firstReaderHoldCount;        HoldCounter rh = cachedHoldCounter;        if (rh != null && rh.tid == current.getId())            return rh.count;        int count = readHolds.get().count;        if (count == 0) readHolds.remove();        return count;    }    /** 从stream重构实例  */    private void readObject(java.io.ObjectInputStream s)        throws java.io.IOException, ClassNotFoundException {        s.defaultReadObject();        readHolds = new ThreadLocalHoldCounter();        setState(0); // reset to unlocked state    }//获取state值    final int getCount() { return getState(); }}</span>
看下公平和非公平策略:

<span style="font-size:18px;">static final class NonfairSync extends Sync {    private static final long serialVersionUID = -8159625535654395037L;    final boolean writerShouldBlock() {        return false; // 非公平的写,可以插队    }    final boolean readerShouldBlock() {        /* 就是检查队列的head的next是不是独占节点  */        return apparentlyFirstQueuedIsExclusive();    }}final boolean apparentlyFirstQueuedIsExclusive() {    Node h, s;    return (h = head) != null &&        (s = h.next)  != null &&        !s.isShared()         &&        s.thread != null;}//公平的就要排队static final class FairSync extends Sync {    private static final long serialVersionUID = -2274990926593161451L;    final boolean writerShouldBlock() {        return hasQueuedPredecessors();    }    final boolean readerShouldBlock() {        return hasQueuedPredecessors();    }}AQS:public final boolean hasQueuedPredecessors() {    // The correctness of this depends on head being initialized    // before tail and on head.next being accurate if the current    // thread is first in queue.    Node t = tail; // Read fields in reverse initialization order    Node h = head;    Node s;    return h != t &&        ((s = h.next) == null || s.thread != Thread.currentThread());}</span>

内部类中的读锁和写锁类也都是调用AQS里面的东西,然后有一些支持超时或中断的方法,其他的一些监控类也不难,都可以看懂。

要记住AQS的int型state拆为2部分:高16位为读锁持有次数(线程的重入由其他变量持有),低16位为写锁的重入次数,共享读,独占写,读锁阻塞写锁,写锁阻塞写锁和读锁,写锁可将级为读锁,读锁不能升级为写锁。最后总结下读写锁的Acquire和release判断大致流程:

写锁Acquire:

1.获取当前线程,state值和写锁重入次数;
2.如果state不为0,说明锁被占用,可能写锁也可能读锁,需要继续判断;
3.在state不为0情况下,如果写锁的重入为0,说明读锁被占用,因为读锁阻塞写锁,所有返回false;
4.在state不为0情况下,如果写锁的重入不为0,说明写锁被占用,因为可重入,所以判断是否为当前线程,不是false;
5.在3、4判断没问题,那就是当前线程写锁重入,就判断下写锁重入后是否大于最大限制,如达到,异常;
6.如5判断没达到最大线程,那就设置写锁重入次数,返回true,获取成功;
7.如果2判断锁没有被持有,基于队列策略判断写是否需要阻塞(非公平时,写不需要阻塞,公平时判断head->next是否null或非当前线程),需要阻塞返回false,挂起,不需要阻塞就cas操作设置state值;
8.如果7需要阻塞或cas设置失败,返回false,挂起;
9.如果7不需要阻塞且cas成功,设置独占线程,返回true,Acquire成功。

写锁release:

1.首先判断是否当前线程持有,否就异常;
2.计算state释放后的值 ;
3.判断释放后的写锁重入是否为0;
4.如果3为true,写锁重入为0那就设置独占线程为null;
5.最后设置AQS的state值,返回3的判断结果。


读锁Acquire:

1.获取当前线程和state锁持有次数
2.线程持有的写锁可降级为读锁,判断有没有其他线程持有写锁,如有,因为写锁阻塞读锁,那就挂起当前线程;
3.如2没有其他线程持有写锁,说明要不写锁没被占用,要不当前线程持有,那就继续,获取读锁的持有;
4.判断3个条件:
4.1)读释放不需要挂起;非公平时判断是否存在head->next为读线程,公平时判断head->next是否null或非当前线程;
4.2)读锁持有小于最大;
4.3)cas设置读锁持有成功
5.如果4的判断都没有问题,继续判断读锁持有是否为0:
5.1)为0表示首次持有读锁,设置2个首次变量缓存首次持有读线程和首次持有读线程的重入次数,这样处理,如果只有一个读的话,以后就不用去查询缓存;
5.2)如果读锁不为0,说明有线程持有读锁,判断当前线程是否是之前缓存的首次持有读线程,如果是,累加缓存的首次持有读线程的重入次数;
5.3)如果上面2个都不满足,那就从缓存的持有变量取当前线程的持有,然后累加重入次数,Acquire成功
6.如4的条件不满足,那就for循环处理当前线程,处理的流程大致同2、3、4、5:
6.1)先判断是否有写锁,如有继续判断是否其他线程持有,如果其他线程持有,那就挂起;
6.2)如果没有线程持有写锁,那就判断读是否要阻塞,如果需要阻塞,继续判断:
6.2.1)已经获取读锁的重入,即使需要阻塞也不管,转到6.3处理,Acquire成功;
6.2.1)如果是其他线程的首次请求,加上上面又判断需要阻塞了,那就Acquire失败,阻塞;
6.3)上面判断Acquire没问题,判断读的持有是否达到最大,最大那就异常,没有下面处理下一些缓存变量,同5的处理,Acquire成功。

读锁release:

1.取当前线程;
2.判断是否已经持有读锁了:
1)如果是,判断重入次数,为1就直接读锁为null,否则递减重入次数;
2)如果不是,那就从缓存的持有里面取当前线程的重入,如果重入小于等于1,需要从持有缓存remove当前线程,这里有个小于等于0的判断,没搞懂什么场景出现,最后递减;
3.for循环设置读锁的持有次数,返回持有次数跟0的比较值。


终于看完AQS部分了,人生不死,学习不止!







参考:

http://ifeve.com/juc-reentrantreadwritelock/#more-9724

http://brokendreams.iteye.com/blog/2250866

0 0
原创粉丝点击