Java多线程并发(三)- 读写锁

  1. 读写锁:通过分离读锁和写锁,使它能提供比排它锁更好的并发性和吞吐量;
  2. jdk5之后提供的读写锁,相比于等待通知机制的实现方式,编程方式更简单;

目录

ReentrantReadWriteLock

实现分析


ReentrantReadWriteLock

特性说明
公平性选择支持非公平和公平的锁获取方式,吞吐量非公平优于公平
重进入支持重进入:读线程获取读锁之后,能再次获取读锁;写线程获取写锁之后,能再次获取写锁,也可以获取读锁
锁降级获取写锁-->获取读锁-->释放写锁,写锁能降级成为读锁

通过实现ReadWriteLock接口,并实现了自身的监控状态的一些方法:类图如下

         

        

展示内部工作状态的方法:

方法描述
getReadLockCount()返回读锁被获取的次数
getReadHoldCount()返回当前线程获取读锁的次数
isWriteLocked()判断写锁是否被获取
getWriteHoldCount()返回当前线程获取写锁的次数

实现分析

  1. 读写状态的设计
  2. 写锁的获取与释放
  3. 读锁的获取与释放
  4. 锁降级

读写状态的设计

通过一个整型变量维护多种状态,该变量被切分成2个部分,高16位表示读,低16位表示写。

0000000000000000000000000000000

0

      |----------------------------------高16位,读状态-----------------------|------------------------------低16位,写状态----------------------------|

如何确定写和读的状态?通过位运算,假设当前状态为s,写状态:s&0x0000FFFF,读状态:s>>>16,当写状态增加1,s+1,当读状态增加1,s+(1<<16),即:s+0x00010000.

写锁的获取与释放

写锁是一个支持重进入的排他锁。如果当前线程在获取写锁时,已经有读锁获取或该线程不是已经获取写锁的线程,则当前线程进入等待状态。

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

​

两个判断条件:1.当前线程是否已经获取了写锁;2.读锁是否存在;

读锁存在,则写锁不能被获取:读写锁要确保写锁的操作对读锁可见,因此只有等待其它读线程释放了读锁,写锁才能被当前线程获取;写锁被获取后,其它读写线程的后续访问被阻塞。

读锁的获取与释放

读锁是一个支持重进入的共享锁。能够被多个线程同时获取,如果当前线程获取读锁,则增加读状态。

protected final int tryAcquireShared(int unused) {
            /*
             * Walkthrough:
             * 1. If write lock held by another thread, fail.
             * 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);
            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);
        }

在获取读锁过程中,如果其他线程已经获取了写锁,则当前线程获取读锁失败,进入等待状态;如果当前线程获取了写锁,则当前线程增加读状态,成功获取读锁。

锁降级

锁降级指写锁降级成为读锁。指把持住写锁,再获取到读锁,随后释放写锁的过程。(中间不能有释放的过程)

中间读锁的获取为什么是必要的?主要是为了保证数据的可见性,如果当前线程不获取读锁而是直接释放写锁,假设有另外一个线程获取写锁,并修改了数据,则当前线程无法感知到其它线程对数据的更新;若当前线程获取读锁,遵循锁降级的步骤,其它线程将被阻塞,直到当前线程使用数据并释放读锁之后,其它线程才能获取写锁进行数据更新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值