ReentrantReadWriteLock类

为了有了ReentrantLock还需要ReentrantReadWriteLock

ReentrantReadWriteLock是一个读写锁,它允许多个读操作同时进行,但在写操作时会阻止其他所有读和写操作。这种锁特别适合于读取远多于写入的场景,因为它可以提高并发性而不会牺牲数据一致性。

  1. 分离的读锁和写锁:ReentrantReadWriteLock提供了两个锁,一个用于读操作(readLock),另一个用于写操作(writeLock)。
  2. 读锁共享性:读锁可以被多个线程同时持有,只要没有线程持有写锁。
  3. 写锁独占性:写锁是独占的,只能由一个线程持有,并且在任何线程持有写锁时,读锁也不能被其他线程持有。
  4. 公平性选择:与ReentrantLock一样,ReentrantReadWriteLock也可以设置为公平模式。

尽管ReentrantLock提供了强大的功能,但它不支持区分读写操作的锁定策略。在某些情况下,使用ReentrantReadWriteLock可能更高效,因为它允许多个线程同时读取共享资源,从而减少了锁竞争的可能性。这对于读多写少的数据结构特别有用,比如缓存、计数器、集合等。

源码分析

在这里插入图片描述

ReentrantReadWriteLock实现了ReadWriteLock接口,ReadWriteLock接口定义了获取读锁和写锁的规范,具体需要实现类去实现;同时其还实现了Serializable接口,表示可以进行序列化,在源代码中可以看到ReentrantReadWriteLock实现了自己的序列化逻辑。Sync继承自AQS、NonfairSync继承自Sync类、FairSync继承自Sync类;ReadLock实现了Lock接口、WriteLock也实现了Lock接口。

内部类分析

  1. ReadLock: 实现了读锁的功能,提供了读取时的锁定和释放操作。当线程请求读锁时,如果当前没有写锁被其他线程持有,则允许获取读锁。
  2. WriteLock: 实现了写锁的功能,提供了写入时的排他锁定和释放操作。当线程请求写锁时,会阻塞直到所有读锁被释放并且没有其他写锁被持有。
  3. FairSync和NonfairSync: 都是 Sync 的子类,FairSync实现了公平的锁获取策略。在公平模式下,等待时间最长的线程将获得锁。NonfairSync实现了非公平的锁获取策略。在非公平模式下,不保证按照请求锁的顺序授予锁,新来的线程可能会插队获取锁。
  4. RWLock: 定义了读写锁的基本操作。
  5. SyncHelper: 用于帮助管理同步状态。
  6. HoldCounter: HoldCounter主要与读锁配套使用。HoldCounter主要有两个属性,count和tid,其中count表示某个读线程重入的次数,tid表示该线程的tid字段的值,该字段可以用来唯一标识一个线程

主要方法

  • tryRelease 该方法用于释放写锁资源,首先会判断该线程是否为独占线程,若不为独占线程,则抛出异常,否则,计算释放资源后的写锁的数量,若为0,表示成功释放,资源不将被占用,否则,表示资源还被占用。
protected final boolean tryRelease(int releases) {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            int nextc = getState() - releases;
            boolean free = exclusiveCount(nextc) == 0;
            if (free)
                setExclusiveOwnerThread(null);
            setState(nextc);
            return free;
        }
  • tryAcquire 用于实现锁的获取逻辑,允许线程以可重入的方式获取锁。其中,变量c为锁状态,变量w表示当前状态c中独占锁的持有数量。如果当前状态不为0,说明锁已经被持有。如果当前锁是共享的(w == 0),或者持有锁的线程不是当前线程,则直接返回false,表示获取失败。前状态不为0,说明锁已经被持有。如果当前线程已经持有锁(判断了重入次数是否超过了最大限制),则简单增加状态计数并返回true,表示获取成功:
  boolean tryAcquire(int acquires) {
            Thread current = Thread.currentThread();
            int c = getState();
            int w = exclusiveCount(c);
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 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;
        }
  • tryReleaseShared 这个方法的作用是尝试释放共享锁(读锁)。
 protected final boolean tryReleaseShared(int unused) {
            Thread current = Thread.currentThread();
           // 当前线程为第一个读线程
            if (firstReader == current) {
                if (firstReaderHoldCount == 1)
                    firstReader = null;
                else
                    firstReaderHoldCount--;
            } else {
                HoldCounter rh = cachedHoldCounter;
                //获取缓存的HoldCounter对象rh,如果rh为空或者rh.tid不等于当前线程的ID,则从readHolds中获取新的HoldCounter对象。
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                int count = rh.count;
                if (count <= 1) {
                //从readHolds中移除当前的HoldCounter对象。
                    readHolds.remove();
                    if (count <= 0)
                        throw unmatchedUnlockException();
                }
                --rh.count;
            }
            for (;;) {
                int c = getState();
                int nextc = c - SHARED_UNIT;
                //尝试更新状态值
                if (compareAndSetState(c, nextc))
                   
                    return nextc == 0;
            }
        }
  • tryAcquireShared 用于读锁线程获取读锁。如果exclusiveCount©不等于0且getExclusiveOwnerThread()不等于当前线程,说明有其他线程持有写锁,此时无法获取共享锁,直接返回-1。如果readerShouldBlock()为false且r(读取次数)小于MAX_COUNT,尝试通过CAS操作更新状态值c,将共享锁计数器加1。如果更新成功,根据不同情况更新firstReader和firstReaderHoldCount的值,并返回1表示获取共享锁成功。
 protected final int tryAcquireShared(int unused) {
           
            Thread current = Thread.currentThread();
            int c = getState();
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            int r = sharedCount(c);
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                if (r == 0) {
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
                    firstReaderHoldCount++;
                } else {
                    HoldCounter rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current))
                        cachedHoldCounter = rh = readHolds.get();
                    else if (rh.count == 0)
                        readHolds.set(rh);
                    rh.count++;
                }
                return 1;
            }
            return fullTryAcquireShared(current);
        }

代码使用示例

ReadThread和WriteThread是自定义的线程类,它们分别代表读线程和写线程。读线程:

public class ReadThread extends Thread{

    private ReentrantReadWriteLock rrwLock;

    public ReadThread(String name, ReentrantReadWriteLock rrwLock) {
        super(name);
        this.rrwLock = rrwLock;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " trying to lock");
        try {
            rrwLock.readLock().lock();
            System.out.println(Thread.currentThread().getName() + " lock successfully");
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rrwLock.readLock().unlock();
            System.out.println(Thread.currentThread().getName() + " unlock successfully");
        }
    }
}

写线程:

public class WriteThread extends Thread{

    private ReentrantReadWriteLock rrwLock;

    public WriteThread(String name, ReentrantReadWriteLock rrwLock) {
        super(name);
        this.rrwLock = rrwLock;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " trying to lock");
        try {
            rrwLock.writeLock().lock();
            System.out.println(Thread.currentThread().getName() + " lock successfully");
        } finally {
            rrwLock.writeLock().unlock();
            System.out.println(Thread.currentThread().getName() + " unlock successfully");
        }
    }
}

使用,创建两个读线程和一个写线程。然后启动这些线程:


        public static void main(String[] args) {
            ReentrantReadWriteLock rrwLock = new ReentrantReadWriteLock();
            ReadThread rt1 = new ReadThread("rt1", rrwLock);
            ReadThread rt2 = new ReadThread("rt2", rrwLock);
            WriteThread wt1 = new WriteThread("wt1", rrwLock);
            rt1.start();
            rt2.start();
            wt1.start();
        }

执行结果:
在这里插入图片描述

  • 15
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

π克

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值