jdk读写锁

误区:

读写锁公平读锁抢占的时候,是去判断,队列中的第一个是不是写锁,如果是,那么就写入。

公平不公平其实体现在是否要直接抢占,如果前面有写锁,而且被感知,无论公平与否,都会阻塞读锁。

获取写锁后,可以降级为读锁。参照

https://www.jianshu.com/p/cd485e16456e

protected final int11 tryAcquireShared(int unused) {

    Thread current = Thread.currentThread();
    int c = getState();
    // exclusiveCount(c) != 0 ---》 用 state & 65535 得到低 16 位的值。如果不是0,说明写锁别持有了。
    // getExclusiveOwnerThread() != current----> 不是当前线程
    // 如果写锁被霸占了,且持有线程不是当前线程,返回 false,加入队列。获取写锁失败。
    // 反之,如果持有写锁的是当前线程,就可以继续获取读锁了。
    if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current)
        // 获取锁失败
        return -1;
    // 如果写锁没有被霸占,则将高16位移到低16位。
    int r = sharedCount(c);// c >>> 16
    // !readerShouldBlock() 和写锁的逻辑一样(根据公平与否策略和队列是否含有等待节点)
    // 不能大于 65535,且 CAS 修改成功
    if (!readerShouldBlock() && r < 65535 && compareAndSetState(c, c + 65536)) {
        // 如果读锁是空闲的, 获取锁成功。
        if (r == 0) {
            // 将当前线程设置为第一个读锁线程
            firstReader = current;
            // 计数器为1
            firstReaderHoldCount = 1;

        }// 如果读锁不是空闲的,且第一个读线程是当前线程。获取锁成功。
         else if (firstReader == current) {// 
            // 将计数器加一
            firstReaderHoldCount++;
        } else {// 如果不是第一个线程,获取锁成功。
            // cachedHoldCounter 代表的是最后一个获取读锁的线程的计数器。
            HoldCounter rh = cachedHoldCounter;
            // 如果最后一个线程计数器是 null 或者不是当前线程,那么就新建一个 HoldCounter 对象
            if (rh == null || rh.tid != getThreadId(current))
                // 给当前线程新建一个 HoldCounter
                cachedHoldCounter = rh = readHolds.get();
            // 如果不是 null,且 count 是 0,就将上个线程的 HoldCounter 覆盖本地的。
            else if (rh.count == 0)
                readHolds.set(rh);
            // 对 count 加一
            rh.count++;
        }
        return 1;
    }
    // 死循环获取读锁。包含锁降级策略。
    return fullTryAcquireShared(current);
}

final int fullTryAcquireShared(Thread current) {
    /*
     * 这段代码与tryAcquireShared中的代码有部分重复,但整体更简单。
     */
    HoldCounter rh = null;
    // 死循环
    for (;;) {
        int c = getState();
        // 如果存在写锁
        if (exclusiveCount(c) != 0) {
            // 并且不是当前线程,获取锁失败,反之,如果持有写锁的是当前线程,那么就会进入下面的逻辑。
            // 反之,如果存在写锁,但持有写锁的是当前线程。那么就继续尝试获取读锁。
            if (getExclusiveOwnerThread() != current)
                return -1;
        // 如果写锁空闲,且可以获取读锁。
        } else if (readerShouldBlock()) {
            // 第一个读线程是当前线程
            if (firstReader == current) {
            // 如果不是当前线程
            } else {
                if (rh == null) {
                    rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current)) {
                        // 从 ThreadLocal 中取出计数器
                        rh = readHolds.get();
                        if (rh.count == 0)
                            readHolds.remove();
                    }
                }
                if (rh.count == 0)
                    return -1;
            }
        }
        // 如果读锁次数达到 65535 ,抛出异常
        if (sharedCount(c) == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        // 尝试对 state 加 65536, 也就是设置读锁,实际就是对高16位加一。
        if (compareAndSetState(c, c + SHARED_UNIT)) {
            // 如果读锁是空闲的
            if (sharedCount(c) == 0) {
                // 设置第一个读锁
                firstReader = current;
                // 计数器为 1
                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;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值