二十五、ReentrantReadWriteLock之读锁加锁失败后流程

43 阅读2分钟

ReentrantReadWriteLock之读锁加锁失败后流程

在fullTryAcquireShared方法前面做的操作,是第一遍尝试获取锁资源,获取失败了并不会退出,而是执行fullTryAcquireShared方法,此方法会再次尝试获取锁资源

final int fullTryAcquireShared(Thread current) {
   
	HoldCounter rh = null;
	for (;;) {
		// 获取state值
		int c = getState();
		// 判断state值的低16位是否不为0
		if (exclusiveCount(c) != 0) {
			// 有线程持有写锁
			if (getExclusiveOwnerThread() != current)
				// 不是当前线程持有写锁
				return -1;
		// 判断是否可以竞争锁资源
		} else if (readerShouldBlock()) {
			// 可以竞争锁资源
			// 无论公平锁还是非公平锁,只要进来,就代表要放到AQS链表中
			// 处理ThreadLocal的内存泄漏问题
			if (firstReader == current) {
				// 当前线程是第一个拿到读锁的,不用处理
			} else {
				// 第一次进来肯定是null
				if (rh == null) {
					rh = cachedHoldCounter;
					if (rh == null || rh.tid != getThreadId(current)) {
						// 当前线程不是最后一个拿到读锁的线程
						// 获取当前线程的重入锁信息
						// 调用get方法,如果此线程是第一次来获取读锁
						// ThreadLocalMap必定没有数据
						// 当ThreadLocalMap中没有数据时,get方法中会调用initialValue方法
						// ThreadLocalHoldCounter对initialValue方法进行了重写
						// 返回一个新的HoldCounter对象
						// 并且将这个对象放到ThreadLocalMap中
						rh = readHolds.get();
						if (rh.count == 0)
							// 当前线程没有拿到过读锁资源,是即将最后一个拿到锁资源的线程
							// 其重入信息会存到cacheHoldCounter中
							// 但是前面的get方法在其ThreadLocalMap中存入了HoldCounter对象
							// 所以需要删除,避免ThreadLocal导致内存泄漏
							readHolds.remove();
					}
				}
				if (rh.count == 0)
					// 进入这里说明当前线程是第一次来竞争读锁
					// 但是if判断语句的readerShouldBlock方法能够返回true
					// 说明现在第一次来竞争读锁的线程不能获取锁,直接退出方法,返回-1
					return -1;
			}
		}
		// 对于公平锁来说,到这里说明同步队列中没有节点
		// 对于非公平锁来说,到这里说明同步队列中没有写线程节点
		// 或者是当前线程来执行重入锁流程
		if (sharedCount(c) == MAX_COUNT)
			throw new Error("Maximum lock count exceeded");
		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 != getThreadId(current))
					rh = readHolds.get();
				else if (rh.count == 0)
					readHolds.set(rh);
				rh.count++;
				cachedHoldCounter = rh;
			}
			return 1;
		}
	}
}