ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【JUC】ReentrantReadWriteLock

2022-07-19 07:33:49  阅读:132  来源: 互联网

标签:JUC Thread ReentrantReadWriteLock System 读锁 线程 println


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/

标签:JUC,Thread,ReentrantReadWriteLock,System,读锁,线程,println
来源: https://www.cnblogs.com/cutter-point/p/16492665.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有