《Java源码分析》:ReadWriteLock(第二部分)

来源:互联网 发布:手持条码数据采集器 编辑:程序博客网 时间:2024/04/29 08:04

《Java源码分析》:ReadWriteLock(第二部分)

本篇博文主要介绍了ReentrantReadWriteLock内部实现。

ReadWriteLock是一个接口,如下:

    public interface ReadWriteLock {        /**         * Returns the lock used for reading.         */        Lock readLock();        /**         * Returns the lock used for writing.         */        Lock writeLock();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

ReentrantReadWriteLock是ReadWriteLock的实现类。

此类的内部是借助于3个类来完成的。如下

1、Sync。NonFairSync/FairSync。

Sync是继承自AbstractQueuedSynchronizer实现的抽象类,NonFairSync/FairSync都是Sync的子类,分别代表非公平同步器和公平同步器。

2、ReadLock

读锁类,当我们读取数据的时候需要加读锁。

3、WriteLock

写锁类,当我们写数据的时候需要加写锁。

写锁和读锁有一些规则,也是其内部实现的基础。

例如:

1、读锁可以同时被多线程所持有,而写锁只能被一个线程持有且持有的同时不允许其他线程持有读、写锁。

2、可重入性

3、可降级性

4、两个锁所持有的个数由AQS状态位state的高低16来记录。

以上的这些特性都是此类实现的内部基础。

下面我们来分析其内部实现,先冲构造开始。

ReentrantReadWriteLock的构造函数

先看ReentrantReadWriteLock类的构造函数

    /**     * Creates a new {@code ReentrantReadWriteLock} with     * default (nonfair) ordering properties.     */    public ReentrantReadWriteLock() {        this(false);//默认创建一个非公平的对象    }       /*        s实例化了AQS的实现类对象,ReadLock、WriteLock对象       */    public ReentrantReadWriteLock(boolean fair) {        sync = fair ? new FairSync() : new NonfairSync();        readerLock = new ReadLock(this);        writerLock = new WriteLock(this);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

公平锁类FairSync和非公平类NonFairSync都没有提供构造函数 
,因此都会调用父类的无参构造函数

       Sync() {            readHolds = new ThreadLocalHoldCounter();            setState(getState()); // ensures visibility of readHolds        }
  • 1
  • 2
  • 3
  • 4

从上面可以看出,如果我们在使用ReentrantReadWriteLock时,使用如下的ReentrantReadWriteLock lock = new ReentrantReadWriteLock();新建一个对象,内部是实例化了sync、readerLock、writeLock三个对象。

WriteLock的lock()的内部实现

先来介绍写锁的lock()/unlock()的内部实现。

WriteLock的lock()的实现源码如下:

        public void lock() {            sync.acquire(1);        }        /*            此函数的思路为:            1、调用tryAcquire获取独占锁,如果没有获取到,则进行2            2、调用addWaiter函数将节点加入队列中,进行3            3、一直自旋,直到其获得锁        */        public final void acquire(int arg) {            if (!tryAcquire(arg) &&                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))                selfInterrupt();        }        protected final boolean tryAcquire(int acquires) {            /*             * Walkthrough:             * 1. If read count nonzero or write count nonzero             *    and owner is a different thread, fail.             * 2. If count would saturate, fail. (This can only             *    happen if count is already nonzero.)             * 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.             */            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;        }        //NonfairSync类中的写一直不阻塞        final boolean writerShouldBlock() {            return false; // writers can always barge        }        //FairSync类中的        final boolean writerShouldBlock() {            return hasQueuedPredecessors();//队列中是否其它线程的节点        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

写锁的lock()和unlock()的过程可以与独占锁ReentrantLock类似来理解,相信看过ReentrantLock源码后的我们理解这些代码是比较容易的了。

大概思想总结一下:

一个线程想获取写锁,所经历的过程如下

1、由于写锁是独占锁,首先肯定是判断写锁有没有被其它线程拥有,由于写锁不能和读锁共存,因此也要判断读锁有没有被其它线程(也包括自己)拥有。如果以上情况均不符合,则此线程得到锁,立即返回并设置AQS状态位。否则进行2

2、将此线程加入到AQS队列中并进行自旋等待获取锁直至此线程获取到锁。

ReadLock的lock()的内部实现

读锁lock()、unlock()方法的思想可以与共享锁Semaphore、CountDownLatch类似来理解。但是比Semaphore、CountDownLatch稍微要复杂一点。

这是因为ReadLock加锁成功与否还和WriteLock有一定的关系。

下面我们具体来看看

        /**         * Acquires the read lock.         *         * <p>Acquires the read lock if the write lock is not held by         * another thread and returns immediately.         *当写锁没有被其它线程持有,则获取一个读锁并立即返回         *如果写锁被其它线程持有,则由于线程调度的原因当前线程被禁用,休眠直至读锁被获取到。         * <p>If the write lock is held by another thread then         * the current thread becomes disabled for thread scheduling         * purposes and lies dormant until the read lock has been acquired.         */        public void lock() {            sync.acquireShared(1);        }        public final void acquireShared(int arg) {            if (tryAcquireShared(arg) < 0)                doAcquireShared(arg);        }        protected final int tryAcquireShared(int unused) {            /*             * Walkthrough:             * 1. If write lock held by another thread, fail.             否则,该线程是有资格获得读锁的,因此根据队列的实际情况此线程是否需要被阻塞,             如果不需要,则获得锁并利用CAS来更新AQS同步队列的状态位。             注意:这一步不检查重入。             * 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.             * 3. If step 2 fails either because thread             *    apparently not eligible or CAS fails or count             *    saturated, chain to version with full retry loop.             */            Thread current = Thread.currentThread();            int c = getState();            if (exclusiveCount(c) != 0 &&                getExclusiveOwnerThread() != current)//写锁被其它的线程占有,则立即返回                return -1;            int r = sharedCount(c);//读锁的个数            /*                如果此线程不应该被阻塞且读锁的个数小于最大锁的限制且CAS设置成功            */            if (!readerShouldBlock() &&                r < MAX_COUNT &&                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 != getThreadId(current))                        cachedHoldCounter = rh = readHolds.get();                    else if (rh.count == 0)                        readHolds.set(rh);                    rh.count++;                }                return 1;            }            return fullTryAcquireShared(current);        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

上面ReadLock加锁的过程还是比较容易理解的哈。唯一不好理解的地方为:HoldCounter 。这里我自己也懒的去查了。以后就机会我再来看下。

以上就是ReadLock、WriteLock的加锁过程。释放锁的过程就不再介绍了,和其它锁的释放原理类似。

阅读全文
0 0
原创粉丝点击