【JUC】ReentrantReadWriteLock

ReentrantReadWriteLock

概述

先带着问题去看这个类:

著作权归https://pdai.tech所有。 链接:https://www.pdai.tech/md/java/thread/java-thread-x-lock-ReentrantReadWriteLock.html

为什么有了ReentrantLock还需要ReentrantReadWriteLock? -- 有共享锁

ReentrantReadWriteLock底层实现原理?

ReentrantReadWriteLock底层读写状态如何设计的? 高16位为读锁,低16位为写锁

读锁和写锁的最大数量是多少? -- 16位

本地线程计数器ThreadLocalHoldCounter是用来做什么的?

答:是这里get就是相当于new了一个对象,用来存放holdCounter的localthread对象

缓存计数器HoldCounter是用来做什么的?

答:是最后一个获取到读锁的线程计数器,每当有新的线程获取到读锁,这个变量都会更新。当最后一个获取读锁的线程重复获取读锁,或者释放读锁,就会直接使用这个变量,速度更快,相当于缓存。

读锁的获取与释放是怎么实现的?

读锁实际就是计数++,释放就是计数-- ,唯一可能影响多线程的地方就是在读锁++,--之后进行状态设置的时候,有个cas操作,可能会有死循环进行操作,那么如果竞争强度很大的时候,可能一直在循环

写锁的获取与释放是怎么实现的?

写锁的可以多次上锁,但是如果是不同的线程进行上锁,那么其他线程的上锁操作就会进入AQS同步队列

写锁如果尝试上锁失败就会挂起线程

释放:

核心是就是判断是否当前执行的线程持有了锁对象(AQS),然后如果是,那么state减去相应的计数即可

RentrantReadWriteLock为什么不支持锁升级?

什么是锁的升降级? RentrantReadWriteLock为什么不支持锁升级?

先要理解什么是锁升级和降级:

著作权归https://pdai.tech所有。 链接:https://www.pdai.tech/md/java/thread/java-thread-x-lock-ReentrantReadWriteLock.html

锁降级指的是写锁降级成为读锁。如果当前线程拥有写锁,然后将其释放,最后再获取读锁,这种分段完成的过程不能称之为锁降级。锁降级是指把持住(当前拥有的)写锁,再获取到读锁,随后释放(先前拥有的)写锁的过程。

读锁和写锁的牵制关系是什么?

当写锁存在的时候,无法获取读锁(除了当前持有写锁的当前线程可以再次获取)

当写锁不存在的时候,读锁可以多次获取

  • 重入:此锁允许reader和writer按照 ReentrantLock 的样式重新获取读取锁或写入锁。在写入线程保持的所有写入锁都已经释放后,才允许重入reader使用读取锁。
    writer可以获取读取锁,但reader不能获取写入锁。
  • 锁降级:重入还允许从写入锁降级为读取锁,实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不可能的。
  • 锁获取的中断:读取锁和写入锁都支持锁获取期间的中断。
  • Condition 支持:写入锁提供了一个 Condition 实现,对于写入锁来说,该实现的行为与 ReentrantLock.newCondition() 提供的 Condition 实现对 ReentrantLock 所做的行为相同。当然,此 Condition 只能用于写入锁。
    读取锁不支持 ConditionreadLock().newCondition() 会抛出 UnsupportedOperationException
  • 监测:此类支持一些确定是读取锁还是写入锁的方法。这些方法设计用于监视系统状态,而不是同步控制。
    对比:

我们发现reentrantReadWriteLock和ReentrantLock类的组成上就差别有读写可重入的锁上多了2个内部类,一个叫readlock一个叫writelock类

这里读写锁的读锁是多线程共享的,即共享锁。

这里读写锁的写锁是在更改时不允许其他线程操作的,也就是排他锁。

构造函数

创建函数默认为不公平锁方式

public ReentrantReadWriteLock(boolean fair) {
    // 构造函数直接初始化读锁和写锁,fair使用的是多台,默认是false
    // 注意这个sync就是对aqs的实现
    sync = fair ? new FairSync() : new NonfairSync();
    readerLock = new ReadLock(this);
    writerLock = new WriteLock(this);
}

// 读锁和写锁的初始化就是把当前对象设置sync
protected ReadLock(ReentrantReadWriteLock lock) {
    sync = lock.sync;
}

protected WriteLock(ReentrantReadWriteLock lock) {
    sync = lock.sync;
}

内部类分析

java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock#lock

获取读锁

public void lock() {
    // 这里的sync对象是非公平锁 --> 注意是reentrantreadwritelock -》 中的NonfairSync
    sync.acquireShared(1);
}

public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}

// java.util.concurrent.locks.ReentrantReadWriteLock.Sync#tryAcquireShared

重点函数分析

读锁是如何上锁的,写锁又是如何上锁的,两者有什么差别?

firstReader的目的是什么,性能如何提升?

为什么读锁可以共享,写锁不可以?

读锁和写锁两个如何合作,会不会有冲突?

什么是锁降级?

tryAcquireShared读锁

java.util.concurrent.locks.ReentrantReadWriteLock.Sync#tryAcquireShared

/**
 * 实现父类接口,尝试获取共享锁
 * 返回如果计数成功,那么就返回1
 *
 * @param unused
 * @return
 */
protected final int tryAcquireShared(int unused) {
	/*
	 * Walkthrough:
	 *
	 * 1. 如果锁被其他线程持有,则失败
	 * 2. 否则,这个线程有资格lock这个wrt的状态,因此ask根据队列策略是否需要阻塞
	 *    如果不需要,尝试通过cas修改状态和更新count来进行授权。
	 *    注意,不检查是否可重入的状态哪一步, 它被推迟到完整版,以避免必须检查保留计数 更典型的非重入情况。
	 * 3. 如果步骤2失败,因为线程显然不合格或CAS失败或计数饱和导致的,则链到版本与完全重试循环。
	 */
	Thread current = Thread.currentThread();
	// 获取状态
	int c = getState();
	// 判断写锁的计数不为0,并且当前持有线程不是自己,那么获取读锁直接失败
	// exclusiveCount 获取低16位数据,如果不为0,说明有写独占线程
	// sharedCount 计算写线程数
	// getExclusiveOwnerThread 当前独占的线程不是自己
	if (exclusiveCount(c) != 0 &&
		getExclusiveOwnerThread() != current)
		return -1;
	// 统计当前读锁个数
	// sharedCount 计算写线程数
	int r = sharedCount(c);
	/**
	 * 调用当前类中的nonfairsync函数中的数据
	 * {@link NonfairSync#readerShouldBlock} 可以获取读锁
	 * 并且读锁数量没有达到上限
	 * compareAndSetState(c, c + SHARED_UNIT) 高位16 + 1 cas操作 --> 读锁(共享锁)个数加1
	 */
	if (!readerShouldBlock() &&
		r < MAX_COUNT &&
		// cas判断能否抢占成功
		compareAndSetState(c, c + SHARED_UNIT)) {
		// r 当前共享单元个数
		if (r == 0) {
			// 设置第一个读锁,设置第一个的目的是为了性能提升?
			// ans: firstReader是获取读锁的第一个线程。如果只有一个线程获取读锁,很明显,使用这样一个变量速度更快
			firstReader = current;
			// 并统计当前读锁所在的线程累加的次数
			firstReaderHoldCount = 1;
		} else if (firstReader == current) {
			// 如果一直是当前线程进行获取读锁,那么计数就一直累加
			firstReaderHoldCount++;
		} else {
			/**
			 * cachedHoldCounter 代表的是最后一个获取读锁的线程的计数器。
			 * 这里get就是相当于new了一个对象
			 * cachedHoldCounter是最后一个获取到读锁的线程计数器,每当有新的线程获取到读锁,这个变量都会更新。
			 * 这个变量的目的是:当最后一个获取读锁的线程重复获取读锁,或者释放读锁,就会直接使用这个变量,速度更快,相当于缓存。
			 * {@link ReentrantReadWriteLock.Sync.ThreadLocalHoldCounter}
			 * {@link HoldCounter}
			 */
			HoldCounter rh = cachedHoldCounter;
			// 如果最后一个线程计数器是 null 或者不是当前线程,那么就新建一个 HoldCounter 对象
			if (rh == null || rh.tid != getThreadId(current)) {
				// 给当前线程新建一个 HoldCounter
				// readHolds 实际是一个threadlocal -- 这里主要是更新了缓存
				cachedHoldCounter = rh = readHolds.get();
			} else if (rh.count == 0) {
				// 如果不是 null,且 count 是 0,就将上个线程的 HoldCounter 覆盖本地的。
				readHolds.set(rh);
			}
			// 对 count 加一
			rh.count++;
		}

		/**
		 * add by xiaof 2022年7月4日09:10:14 如果不考虑缓存,是否可以这样写
		 * 后续所有用到的逻辑都要统一一下,这里主要的目的就是计数,并且前面有做cas操作
		 * {@link ReentrantReadWriteLock.Sync#readCount}
		 */
//                readCount++;
		return 1;
	}
	// 如果cas抢占不成功/线程满了,  死循环获取读锁。包含锁降级策略。
	return fullTryAcquireShared(current);
}
tryAcquire写锁获取

java.util.concurrent.locks.ReentrantReadWriteLock.Sync#tryAcquire

protected final boolean tryAcquire(int acquires) {

    Thread current = Thread.currentThread();
    // 获取计数状态
    int c = getState();
    // 获取写锁数量,也就是低16位的个数
    int w = exclusiveCount(c);
    // 判断锁计数不为0
    if (c != 0) {
        // (Note: if c != 0 and w == 0 then shared count != 0)
        /**
		 * 只要状态不为0,那么在上锁的时候就会调用 acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 方法
		 * {@link AbstractQueuedSynchronizer#acquireQueued(java.util.concurrent.locks.AbstractQueuedSynchronizer.Node, int)}
		 */
        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;
    }
    /**
	 * {@link NonfairSync#writerShouldBlock()}
	 * 如果计数为0,第一个函数返回永远为 false
	 * 第二个是cas操作,吧计数++
	 */
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    // 设置锁的持有对象为当前线程
    setExclusiveOwnerThread(current);
    return true;
}
fullTryAcquireShared 获取读锁失败重试

当存在2个线程竞争获取写锁,然后有一个已经获取到读锁,另外一个读锁在同步队列中的时候触发

/**
 * Full version of acquire for reads, that handles CAS misses
 * and reentrant reads not dealt with in tryAcquireShared.
 *
 * reads线程满了,或者cas无法设置成功,如果可重入锁没有再tryacquire中处理的情况
 */
final int fullTryAcquireShared(Thread current) {
	/**
	 * {@link Sync#tryAcquireShared(int)}
	 * 这里部分代码和tryAcquireShared中的人方法有冗余,但是整体上更简单(说白了就是一样的代码)
	 */
	HoldCounter rh = null;
	// 这里是一个自旋重试
	for (;;) {
		// 获取读写锁状态数据
		int c = getState();
		// exclusiveCount 获取写锁个数
		if (exclusiveCount(c) != 0) {
			if (getExclusiveOwnerThread() != current)
				return -1;
			// else we hold the exclusive lock; blocking here
			// would cause deadlock.
		} else if (readerShouldBlock()) {
			// Make sure we're not acquiring read lock reentrantly
			// 写锁空闲  且  公平策略决定 线程应当被阻塞
			// 下面的处理是说,如果是已获取读锁的线程重入读锁时,
			// 即使公平策略指示应当阻塞也不会阻塞。
			// 否则,这也会导致死锁的。
			if (firstReader == current) {
				// assert firstReaderHoldCount > 0;
			} else { // 当前线程不为第一个读线程,上面那个判断
				if (rh == null) {
					rh = cachedHoldCounter;
					// 计数器为空或者计数器的tid不为当前正在运行的线程的tid
					if (rh == null || rh.tid != getThreadId(current)) {
						rh = readHolds.get();
						if (rh.count == 0)
							readHolds.remove();
					}
				}
				if (rh.count == 0)
					return -1;
			}
		}
		// 读锁数量达到最多
		if (sharedCount(c) == MAX_COUNT)
			throw new Error("Maximum lock count exceeded");
		if (compareAndSetState(c, c + SHARED_UNIT)) {
			// 申请读锁成功,下面的处理跟tryAcquireShared是类似的。
			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; // cache for release
			}
			return 1;
		}
	}
}

实战样例

package com.juc.y2022.aqs.ReentrantReadWriteLock;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 功能描述
 *
 * @since 2022-06-29
 */
public class Code01_ReadLockMAX {

    public static void main(String[] args) {
        // 读锁是什么,如何上锁的?
        // 为什么说可以共享?如何实现共享
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

        int count = 0;
        // 最大上线 read 第65534次上锁
        // 第65535次就会报错
        int MAX = (1 << 16);
        int BREAK = 65535;
        for (int i = 0; i < MAX; i++) {
            if (i == BREAK) {
                System.out.println("break point");
            }
            System.out.println("read 第" + count++ + "次上锁");
            reentrantReadWriteLock.readLock().lock();
        }
        System.out.println(count);
    }

}


package com.juc.y2022.aqs.ReentrantReadWriteLock;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;

/**
 * 功能描述
 *
 * @since 2022-06-29
 */
public class Code02_ReadLockOtherGetReadLock {

    public static void main(String[] args) {
        // 读锁是什么,如何上锁的?
        // 为什么说可以共享?如何实现共享
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

        AtomicInteger count = new AtomicInteger();
        int MAX = (1 << 16);
        int BREAK = 65535;
        // 最大上线 read 第65534次上锁
        // 第65535次就会报错
        IntStream.range(0, MAX - 1).forEach(x -> new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
            if (count.get() == BREAK) {
                System.out.println("break point");
            }
            reentrantReadWriteLock.readLock().lock();
        }).start());
        System.out.println(count.get());
    }

}



package com.juc.y2022.aqs.ReentrantReadWriteLock;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;

/**
 * 功能描述
 *
 * @since 2022-07-11
 */
public class Code03_ReadLockLockAndUnlock {

    public static void main(String[] args) {
        // 读锁是什么,如何上锁的?
        // 为什么说可以共享?如何实现共享
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();

        AtomicInteger count = new AtomicInteger();
        int MAX = (1 << 16);
        int BREAK = 65535;
        // 最大上线 read 第65534次上锁
        // 第65535次就会报错
        System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
        if (count.get() == BREAK) {
            System.out.println("break point");
        }
        readLock.lock();
        readLock.lock();
        // 解锁
        System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解锁");
        readLock.unlock();
        readLock.lock();

        IntStream.range(0, MAX - 1).forEach(x -> new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
            if (count.get() == BREAK) {
                System.out.println("break point");
            }
            readLock.lock();
            // 解锁
            System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解锁");
            readLock.unlock();
        }).start());
        System.out.println(count.get());
    }

}


package com.juc.y2022.aqs.ReentrantReadWriteLock;

import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;

/**
 * 功能描述
 *
 * @since 2022-07-11
 */
public class Code04_WriteLockLockAndUnlock {

    static AtomicInteger count = new AtomicInteger();
    static int MAX = (1 << 16);
    static int BREAK = 65535;

    public static void main(String[] args) {
        // 读锁是什么,如何上锁的?
        // 为什么说可以共享?如何实现共享
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();

//        lockStateNotZeroAndLock(reentrantReadWriteLock);
//        writeLockAndUnlock(reentrantReadWriteLock);
//        otherThreadWriteLock(reentrantReadWriteLock);
        otherThreadWriteLockAndUnlock(reentrantReadWriteLock);

        // 最大上线 read 第65534次上锁
        // 第65535次就会报错
//        IntStream.range(0, MAX - 1).forEach(x -> new Thread(() -> {
//            System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
//            if (count.get() == BREAK) {
//                System.out.println("break point");
//            }
//            writeLock.lock();
//            // 解锁
//            System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解锁");
//            writeLock.unlock();
//        }).start());
//        System.out.println(count.get());
    }

    public static void lockStateNotZeroAndLock(ReentrantReadWriteLock reentrantReadWriteLock) {
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
        readLock.lock();
        readLock.lock();
        writeLock.lock();
        // 解锁
        System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解锁");
        writeLock.unlock();
        writeLock.lock();
    }

    public static void writeLockAndUnlock(ReentrantReadWriteLock reentrantReadWriteLock) {
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
        writeLock.lock();
        // 解锁
        System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解锁");
        writeLock.unlock();
    }

    public static void otherThreadWriteLock(ReentrantReadWriteLock reentrantReadWriteLock) {
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
        writeLock.lock();
        writeLock.lock();

        // 另外一个线程进行上锁
        IntStream.range(0, 2).forEach(x -> new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
            writeLock.lock();
        }).start());
        System.out.println(count.get());

    }

    public static void otherThreadWriteLockAndUnlock(ReentrantReadWriteLock reentrantReadWriteLock) {
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        // 另外一个线程进行上锁
        IntStream.range(0, 2).forEach(x -> new Thread(() -> {
            while (true) {
                System.out.println(Thread.currentThread().getName() + ":writeLock 第" + count.getAndIncrement() + "次上锁");
                writeLock.lock();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int flag = 0;
                IntStream.range(0, flag).forEach(xx -> {
                    System.out.println(Thread.currentThread().getName() + ":writeLock 第" + count.getAndDecrement() + "次解锁:" + xx);
                    writeLock.unlock();
                });
            }
        }).start());

    }

    static int getChoiceWithTimeout(int timeout) {
        Callable<Integer> k = () -> {
            return new Scanner(System.in).nextInt();
        };
        Long start = System.currentTimeMillis();
        int choice = 0;
        boolean valid;
        ExecutorService l = Executors.newFixedThreadPool(1);
        Future<Integer> g;
        g = l.submit(k);
        done: while (System.currentTimeMillis() - start < timeout) {
            do {
                valid = true;
                if (g.isDone()) {
                    try {
                        choice = g.get();
                        break done;
                    } catch (InterruptedException | ExecutionException | IllegalArgumentException e) {
                       e.printStackTrace();
                    }
                }
            } while (!valid);
        }
        g.cancel(true);
        return choice;
    }

}


package com.juc.y2022.aqs.ReentrantReadWriteLock;

import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;

/**
 * 功能描述
 *
 * @since 2022-07-11
 */
public class Code05_WRGetLock {

    static AtomicInteger count = new AtomicInteger();
    static int MAX = (1 << 16);
    static int BREAK = 65535;

    public static void main(String[] args) {
        // 读锁是什么,如何上锁的?
        // 为什么说可以共享?如何实现共享
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
//        wrgetlockWait(reentrantReadWriteLock);
        readgetlockWait(reentrantReadWriteLock);

    }

    public static void wrgetlockWait(ReentrantReadWriteLock reentrantReadWriteLock) {
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        writeLock.lock();
        // **当写锁存在的时候,无法获取读锁(除了当前持有写锁的当前线程可以再次获取)**
        IntStream.range(0, 2).forEach(x -> new Thread(() -> {
            try {
                System.out.println(Thread.currentThread() + ":在写锁存在的情况下获取读锁");

                if (readLock.tryLock(4, TimeUnit.SECONDS)) {
                    System.out.println(Thread.currentThread() + ":在写锁存在的情况下获取读锁" + ":success");
                } else {
                    System.out.println(Thread.currentThread() + ":在写锁存在的情况下获取读锁" + ":fail");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }).start());

        getChoiceWithTimeout(1000 * 100);
    }

    public static void readgetlockWait(ReentrantReadWriteLock reentrantReadWriteLock) {
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        // **当写锁存在的时候,无法获取读锁(除了当前持有写锁的当前线程可以再次获取)**
        IntStream.range(0, 2).forEach(x -> new Thread(() -> {
            try {
                System.out.println(Thread.currentThread() + ":在写锁存在的情况下获取读锁");

                if (readLock.tryLock(4, TimeUnit.SECONDS)) {
                    System.out.println(Thread.currentThread() + ":在写锁存在的情况下获取读锁" + ":success");
                } else {
                    System.out.println(Thread.currentThread() + ":在写锁存在的情况下获取读锁" + ":fail");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }).start());
    }

    static int getChoiceWithTimeout(int timeout) {
        Callable<Integer> k = () -> {
            return new Scanner(System.in).nextInt();
        };
        Long start = System.currentTimeMillis();
        int choice = 0;
        boolean valid;
        ExecutorService l = Executors.newFixedThreadPool(1);
        Future<Integer> g;
        g = l.submit(k);
        done: while (System.currentTimeMillis() - start < timeout) {
            do {
                valid = true;
                if (g.isDone()) {
                    try {
                        choice = g.get();
                        break done;
                    } catch (InterruptedException | ExecutionException | IllegalArgumentException e) {
                        e.printStackTrace();
                    }
                }
            } while (!valid);
        }
        g.cancel(true);
        return choice;
    }

}


package com.juc.y2022.aqs.ReentrantReadWriteLock;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;

/**
 * 功能描述
 * 锁降级:重入还允许从写入锁降级为读取锁,实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,**从读取锁升级到写入锁是不可能的。**
 *
 * @since 2022-07-11
 */
public class Code06_UpAndDownLock {

    static AtomicInteger count = new AtomicInteger();
    static int MAX = (1 << 16);
    static int BREAK = 65535;

    public static void main(String[] args) {
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
        tryDownLock(reentrantReadWriteLock);

    }

    public static void tryDownLock(ReentrantReadWriteLock reentrantReadWriteLock) {
        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();

        IntStream.range(0, 1).forEach(x -> new Thread(() -> write(readLock, writeLock)).start());
        IntStream.range(0, 1).forEach(x -> new Thread(() -> read(readLock, writeLock)).start());

    }

    public static void read(ReentrantReadWriteLock.ReadLock readLock, ReentrantReadWriteLock.WriteLock writeLock) {

        System.out.println(Thread.currentThread() + " begin get read lock");
        readLock.lock();

        try {
            System.out.println(Thread.currentThread() + " get read lock, begin execute");
            Thread.sleep(20);
            System.out.println(Thread.currentThread() + " try up read lock as write lock");
            // 读锁升级
            writeLock.lock();
            System.out.println(Thread.currentThread() + "  读锁升级为写锁成功");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            readLock.unlock();
            System.out.println(Thread.currentThread() + "  释放读锁");
        }

    }

    public static void write(ReentrantReadWriteLock.ReadLock readLock, ReentrantReadWriteLock.WriteLock writeLock) {

        System.out.println(Thread.currentThread() + " begin get 写 lock");
        writeLock.lock();

        try {
            System.out.println(Thread.currentThread() + " get 写 lock, begin execute");
            Thread.sleep(40);
            System.out.println(Thread.currentThread() + " try up read lock as write lock");
            // 写锁降级读锁
            readLock.lock();
            System.out.println(Thread.currentThread() + "  写锁降级为读锁成功");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread() + "  释放写锁");
            writeLock.unlock();
            readLock.unlock();
        }

    }

}



package com.juc.y2022.aqs.ReentrantReadWriteLock;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;

/**
 * 功能描述
 * 获取读锁失败,重试
 *
 * @since 2022-07-11
 */
public class Code07_ReadLockFail {

    static AtomicInteger count = new AtomicInteger();
    static int MAX = (1 << 16);
    static int BREAK = 65535;

    public static void main(String[] args) {
        ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
//        fulltry(reentrantReadWriteLock);
        fulltry2(reentrantReadWriteLock);

    }

    public static void fulltry(ReentrantReadWriteLock reentrantReadWriteLock) {

        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        // **当写锁存在的时候,无法获取读锁(除了当前持有写锁的当前线程可以再次获取)**
        IntStream.range(0, 2).forEach(x -> new Thread(() -> {
            try {
                System.out.println(Thread.currentThread() + ": 获取写锁");
                writeLock.lock();
                System.out.println(Thread.currentThread() + ": 获取写锁-success");
                System.out.println(Thread.currentThread() + ": 获取读锁");
                readLock.lock();
                System.out.println(Thread.currentThread() + ": 获取读锁-success");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start());

    }

    public static void fulltry2(ReentrantReadWriteLock reentrantReadWriteLock) {

        ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
        ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
        // **当写锁存在的时候,无法获取读锁(除了当前持有写锁的当前线程可以再次获取)**
        IntStream.range(0, 22).forEach(x -> new Thread(() -> {
            try {
                System.out.println(Thread.currentThread() + ": 获取读锁");
                readLock.lock();
                System.out.println(Thread.currentThread() + ": 获取读锁-success");

                int random1 = (int) (Math.random() * 10);
                if (random1 >= 9) {
                    System.out.println(Thread.currentThread() + ": 获取写锁");
                    writeLock.lock();
                    System.out.println(Thread.currentThread() + ": 获取---------写锁-success");
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start());

    }

}


总结

为什么读锁是共享锁:
根本原因是state计数的位置不同,共享锁是int的高位(前16)计数的,而且不会去判断是否已经有读锁,直接计数++即可

为什么写锁是排他锁:
state计数的是低位的16位,并且会判断是否已经存在持有写锁,如果有其他线程持有写锁,那么就会进入同步队列等待

参考

https://www.pdai.tech/md/java/thread/java-thread-x-lock-ReentrantReadWriteLock.html

https://love1024.blog.csdn.net/article/details/80235197

https://blog.csdn.net/fanrenxiang/article/details/104312606

http://ifeve.com/juc-reentrantreadwritelock/

posted @ 2022-07-19 07:25  cutter_point  阅读(71)  评论(0编辑  收藏  举报