java 从零手写实现 ReadWriteLock 读写锁( 二 )

<>();/*** volatile 引用 , 保证线程间的可见性+易变性** @since 0.0.2*/private final AtomicReference writeOwner = new AtomicReference<>();/*** 写次数统计*/private int writeCount = 0;}获取读锁/** * 获取读锁,读锁在写锁不存在的时候才能获取 * * @since 0.0.2 */@Overridepublic synchronized void lockRead() {try {// 写锁存在,需要waitwhile (!tryLockRead()) {log.debug("获取读锁失败 , 进入等待状态 。 ");wait();}} catch (InterruptedException e) {Thread.interrupted();}}/** * 尝试获取读锁 * * 读锁之间是不互斥的 , 这里后续需要优化 。* * @return 是否成功 * @since 0.0.2 */private boolean tryLockRead() {if (writeCount > 0) {log.debug("当前有写锁 , 获取读锁失败");return false;}Thread currentThread = Thread.currentThread();// 次数暂时固定为1 , 后面如果实现可重入 , 这里可以改进 。this.readCountMap.put(currentThread, 1);return true;}每次尝试获取读锁的时候 , 我们都将当前线程作为 key 放入 readCountMap 中 , 对应的值暂时为1 。
释放读锁释放读锁的时候 , 我们就会进行归属权校验 。
如果获取失败 , 则说明不是当前锁的持有者 , 则直接释放失败 。
/** * 释放读锁 * * @since 0.0.2 */@Overridepublic synchronized void unlockRead() {Thread currentThread = Thread.currentThread();Integer readCount = readCountMap.get(currentThread);if (readCount == null) {throw new RuntimeException("当前线程未持有任何读锁 , 释放锁失败!");} else {log.debug("释放读锁 , 唤醒所有等待线程 。 ");readCountMap.remove(currentThread);notifyAll();}}获取写锁/** * 获取写锁 * * @since 0.0.2 */@Overridepublic synchronized void lockWrite() {try {// 写锁存在,需要waitwhile (!tryLockWrite()) {wait();}// 此时已经不存在获取写锁的线程了,因此占坑,防止写锁饥饿writeCount++;} catch (InterruptedException e) {Thread.interrupted();}}/** * 尝试获取写锁 * * @return 是否成功 * @since 0.0.2 */private boolean tryLockWrite() {if (writeCount > 0) {log.debug("当前有其他写锁 , 获取写锁失败");return false;}// 读锁if (!readCountMap.isEmpty()) {log.debug("当前有其他读锁 , 获取写锁失败 。 ");return false;}Thread currentThread = Thread.currentThread();boolean result = writeOwner.compareAndSet(null, currentThread);log.debug("尝试获取写锁结果:{}", result);return result;}尝试获取写锁时 , 判断是否有写的条件不变 。
如果 readCountMap 不为空 , 则说明存在写锁 。
通过 CAS 设置对应的写线程持有信息 , 返回是否设置成功 。
释放写锁释放写锁的逻辑和原来类似 , 只不过添加了一个 owner 持有权的校验 。
/** * 释放写锁 * * @since 0.0.2 */@Overridepublic synchronized void unlockWrite() {boolean toNullResult = writeOwner.compareAndSet(Thread.currentThread(), null);if (toNullResult) {writeCount--;log.debug("写锁释放 , 唤醒所有等待线程 。 ");notifyAll();} else {throw new RuntimeException("释放写锁失败");}}可重入的支持说明我们上一小节实现了一个支持验证锁持有者的读写锁 。
下面来看一下如何实现一个可重入的读写锁 。
类定义/** * 读写锁实现-可重入锁 * * @author binbin.hou * @since 0.0.2 */public class LockReadWriteRe implements IReadWriteLock {private static final Log log = LogFactory.getLog(LockReadWriteRe.class);/*** 如果使用类似 write 的方式 , 会导致读锁只能有一个 。* 调整为使用 HashMap 存放读的信息** @since 0.0.2*/private final Map readCountMap = new HashMap<>();/*** volatile 引用 , 保证线程间的可见性+易变性** @since 0.0.2*/private final AtomicReference writeOwner = new AtomicReference<>();/*** 写次数统计*/private int writeCount = 0;}基本的属性和上一小节是一样的 。
获取读锁/** * 获取读锁,读锁在写锁不存在的时候才能获取 * * @since 0.0.2 */@Overridepublic synchronized void lockRead() {try {// 写锁存在,需要waitwhile (!tryLockRead()) {log.debug("获取读锁失败 , 进入等待状态 。 ");wait();}} catch (InterruptedException e) {Thread.interrupted();}}/** * 尝试获取读锁 * * 读锁之间是不互斥的 , 这里后续需要优化 。** @return 是否成功 * @since 0.0.2 */private boolean tryLockRead() {if (writeCount > 0) {log.debug("当前有写锁 , 获取读锁失败");return false;}Thread currentThread = Thread.currentThread();Integer count = readCountMap.get(currentThread);if(count == null) {count = 0;}// 可重入实现count++;this.readCountMap.put(currentThread, count);return true;}这里和以前的区别就是支持可重入 , 通过 count 来维护每一个线程对应的读总数 。
释放读锁/** * 释放读锁 * * @since 0.0.2 */@Overridepublic synchronized void unlockRead() {Thread currentThread = Thread.currentThread();Integer readCount = readCountMap.get(currentThread);if (readCount == null) {throw new RuntimeException("当前线程未持有任何读锁 , 释放锁失败!");} else {readCount--;// 已经是最后一次if(readCount == 0) {readCountMap.remove(currentThread);} else {readCountMap.put(currentThread, readCount);}log.debug("释放读锁 , 唤醒所有等待线程 。 ");notifyAll();}}