读写锁-ReadWriteLock

一.读写状态的设计

(ReadWriteLock是个接口,ReentrantReadWriteLock是该接口的具体实现类)

回想之前ReentrantLock重入锁中,同步状态state表示锁被一个线程重复获取的次数。
读写锁的同步状态state表示维护多个读线程和一个写线程的状态,这个同步状态的高16位表示读状态,低16位表示写状态

假设当前同步状态值为S,写状态等于S & 0x0000FFFF(将高16位全部抹去),读状态等于S>>>16(无符号补0右移16位)。当写状态增加1时,等于S+1(低16位的),当读状态增加1时,等于S+(1<<16), 添加到高16位中

二.写锁的获取和释放

写锁是一个支持重进入的排它锁。如果已经获取了写锁的线程再次去获取写锁,则增加写状态。
在下面两种情况下线程进入等待状态:

  1. 当前线程在获取写锁时,读锁已经被获取(读状态不为0,写状态为0);
  2. 或者该线程不是已经获取写锁的线程;
写锁的获取:
protected final boolean tryAcquire(int acquires)           
            Thread current = Thread.currentThread();
            int c = getState();
            int w = exclusiveCount(c);
            if (c != 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;
        }

分析
如果存在读锁,则写锁不能被获取,原因在于:读写锁要保证写锁的操作对读锁可见,如果允许读锁在已被获取的情况下对写锁的获取,那么正在运行的其他线程就无法感知到当前写线程的操作(数据没有使用voliate修饰)。因此,只有等待其他线程都释放了读锁,写锁才能被当前线程获取,而写锁一旦被获取,则其他线程的后续访问均被阻塞。

写锁的释放
写锁的释放与ReentrantLock的释放过程类似,每次释放均减少写状态,当写状态为0时表示写锁已被释放,从而等待的读写线程能够继续访问读写锁,同时前次写线程的修改对后续线程可见 (数据未使用volatile修饰时的保证)。

三.读锁的获取与释放

读锁是一个支持重进入的共享锁。它能被多个线程同时访问。

获取锁时(tryAcquireShared方法):如果当前线程已经获取过了则增加读状态(是使用CAS操作线程安全的,因为是可能同时存在多个线程进入读操作),如果其他线程获取了写锁,则当前线程进入等待状态。

读状态是所有线程获取读锁次数的总和,而每个线程各自获取读锁的次数只能选择保存在ThreadLocal中,由线程自身维护,例如getReadHoldCount()方法返回当前线程获取读锁的次数。

释放锁(tryReleaseShared方法): 每次释放均减少读状态(线程安全的,可能同时有多个线程释放读锁),减少的值是(1<<16), 高16位减少1.

四.锁降级

锁降级是指写锁降级成为读锁。过程为:当前线程持有写锁,再获取到读锁,随后释放写锁的过程;如果是当前线程拥有写锁,然后将其释放,最后再获取读锁,这种分段完成的就不是锁降级

一个问题锁降级中的获取读锁是否必要吗
答案:为了保证数据的可见性,如果当前线程不获取读锁而是直接释放锁,假设另外一个线程(假设为线程T)获取了写锁并修改了数据,那么当前线程无法感知到线程T的数据更新 (数据没有使用voliate修饰)。
如果当前线程获取读锁,即遵循锁降级的步骤,则线程T将会被阻塞,直到当前线程使用完数据并释放读锁之后,线程T才能获取写锁进行数据更新(此时的写操作对其他线程可见)。

ReentrantReadWriteLock不支持锁升级(先持有读锁,然后获取写锁,最后释放读锁的过程)目的也是保证数据可见性,如果读锁已被多个线程获取,其中任意线程成功获取了写锁并更新了数据,则其更新对其他获取了读锁的线程是不可见的(数据未使用volatile修饰)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值