首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多线程8 读写锁ReentrantReadWriteLock加解锁

多线程8 读写锁ReentrantReadWriteLock加解锁

作者头像
用针戳左手中指指头
发布2021-01-29 11:07:29
4030
发布2021-01-29 11:07:29
举报
文章被收录于专栏:学习计划学习计划

读锁不可以升级,写锁可以降级?

读锁是可并行的,写锁是串行的,那么如果多个读锁并行执行,遇到升级语句,就会出现死锁,比如t1要升级,那么就要等t2释放锁,而t2正好也在当t1释放锁。

加锁:

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
    Thread current = Thread.currentThread();
// 线程计数
    int c = getState();
// 获取独占锁,就是写锁
    int w = exclusiveCount(c);
// 线程计数!=0,表示重入;这里有两种:读锁,写锁
    if (c != 0) {
//  如果是读锁,或者当前线程并非加锁线程,返回false,就会进入acquireQueued(addWaiter(Node.EXCLUSIVE), arg))获取锁
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
// 判断写锁的可重入次数是否超过MAX_COUNT,超过抛异常 
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
// 到这里,确定是写锁且当前线程已持有锁,重入锁+1
        setState(c + acquires);
        return true;
    }
// 走到这里,表示c=0,即没有写锁,也没有读锁
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    setExclusiveOwnerThread(current);
    return true;
}
final boolean writerShouldBlock() {
// 这个方法就是上一篇锁讲过的,查询是否有线程等待,有就排队
    return hasQueuedPredecessors();
}
if (writerShouldBlock() ||
// 设置线程计数
        !compareAndSetState(c, c + acquires))
        return false;
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
// 然后进入这个方法,上一篇讲过
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

这里就是,队列里排队获取锁,和reentrantLock一样。

读锁:

public void lock() {
    sync.acquireShared(1);
}
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}
protected final int tryAcquireShared(int unused) {
    Thread current = Thread.currentThread();
    int c = getState();
// 判断是否独占锁,是否当前线程;这里如果出现独占锁,那么该线程就返回-1,去排队
// 为什么呢?因为在读锁加锁的时候,也可能出现写锁进来,如果写锁进来了,写锁是排他锁,独占一把锁,那么读锁也要去排队
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
// 非独占锁(写锁),获取计数
    int r = sharedCount(c);
// 没有线程等待,
    if (!readerShouldBlock() &&
// 且读锁计数小于最大值,
        r < MAX_COUNT &&
// 且加锁成功
        compareAndSetState(c, c + SHARED_UNIT)) {
// 读锁计数为0,没有读锁,第一个线程进来
        if (r == 0) {
// 就给第一个线程对象赋值,
            firstReader = current;
            firstReaderHoldCount = 1;
// 如果说第一个线程对象等于当前线程对象,就是重入锁
        } else if (firstReader == current) {
// 那么第一个线程内部计数+1
            firstReaderHoldCount++;
// 如果以上条件都否定,即不是第一个线程,也不是重入锁
        } else {
// 第一次进来,重入锁计数对象 = null
            HoldCounter rh = cachedHoldCounter;
// 当前缓存中还没有,或者是第二次进来,rh不会空,那么判断rh的线程id是否和当前线程id相同,不同则表示其他线程进入
            if (rh == null || rh.tid != getThreadId(current))
// 拿到缓存的重入锁对象:如果是同一个线程进入,就返回那个线程的缓存计数对象,如果是其他线程,就会初始化一个返回
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                readHolds.set(rh);
// 缓存对象计数+1
            rh.count++;
        }
        return 1;
    }
    return fullTryAcquireShared(current);
}

当前线程可重入锁的数量是内部是线程变量实现;

它自己本身保存了一份ThreadLocalMap对象,用于保存重入锁的线程

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
// 有当前线程
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
// 拿不到就初始化一个
    return setInitialValue();
}

假设第三个线程:

protected final int tryAcquireShared(int unused) {
    Thread current = Thread.currentThread();
    int c = getState();
// 判断是否独占锁,是否当前线程;这里如果出现独占锁,那么该线程就返回-1,去排队
// 为什么呢?因为在读锁加锁的时候,也可能出现写锁进来,如果写锁进来了,写锁是排他锁,独占一把锁,那么读锁也要去排队
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
// 非独占锁(写锁),获取计数
    int r = sharedCount(c);
// 没有线程等待,
    if (!readerShouldBlock() &&
// 且读锁计数小于最大值,
        r < MAX_COUNT &&
// 且加锁成功
        compareAndSetState(c, c + SHARED_UNIT)) {
// 读锁计数为0,没有读锁,第一个线程进来
        if (r == 0) {
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            firstReaderHoldCount++;
        } else {
// 第三次rh对象不为空
            HoldCounter rh = cachedHoldCounter;
// rh != null,但tid不同 判断为true
            if (rh == null || rh.tid != getThreadId(current))
// 从缓存对象中获取到缓存对象;存在返回,不存在初始化
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                readHolds.set(rh);
// 缓存对象计数+1,这时缓存对象中第三个的计数是1
            rh.count++;
        }
        return 1;
    }
    return fullTryAcquireShared(current);
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档