读锁应该插队吗?什么是锁的升降级?

背景

ReentrantReadWriteLock可以设置公平或非公平,为什么?

读锁插队策略

每次获取响应锁之前都要检查能否获取

  • readerShouldBlock
  • writerShouldBlock

公平锁

final boolean writerShouldBlock() { return hasQueuedPredecessors();
} final boolean readerShouldBlock() { return hasQueuedPredecessors();
}

由此可见只要等待队列中有线程等待,也就是hasQueuedPredecessors返回true时,writer和reader都会block,乖乖排队

非公平锁

final boolean writerShouldBlock() { return false; // writers can always barge } final boolean readerShouldBlock() { return apparentlyFirstQueuedIsExclusive();
}
final boolean apparentlyFirstQueuedIsExclusive() {
    Node h, s; return (h = head) != null &&
        (s = h.next)  != null &&
        !s.isShared()         &&
        s.thread != null;
}
  • 写锁始终是false,由此可见想要获取写锁随时可以插队
  • 读锁



T1,T2正在持有读锁,T3请求写锁,于是进入等待,这时候T4插队获取读锁,允许T4插队还是不插队呢?

1.允许

少量线程插队确实可以提升效率,但是

问题:如果读取线程不断增加,线程5,6,7,8,9都来请求,读锁长时间不释放,线程3进入饥饿状态,我写操作还执不执行了?

2.不允许

都有机会运行,都不会等待太久时间

import java.util.concurrent.locks.ReentrantReadWriteLock; class ReadLockJumpQueue { private static final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); private static final ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock(); private static final ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock(); private static void read() {
        readLock.lock(); try {
            System.out.println(Thread.currentThread().getName() + "得到读锁,正在读");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + "释放读锁");
            readLock.unlock();
        }
    } private static void write() {
        writeLock.lock(); try {
            System.out.println(Thread.currentThread().getName() + "得到写锁,正在写");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + "释放写锁");
            writeLock.unlock();
        }
    } public static void main(String[] args) throws InterruptedException { new Thread(() -> read(), "Thread-2").start(); new Thread(() -> read(), "Thread-4").start(); new Thread(() -> write(), "Thread-3").start(); new Thread(() -> read(), "Thread-5").start();
    }
}

锁的升降级

读写锁的降级

import java.util.concurrent.locks.ReentrantReadWriteLock; public class CachedData {
    Object data; volatile boolean cacheValid; final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); void processCachedData() { //首先获取读锁 rwl.readLock().lock(); //判断缓存是否有效,有效直接输出 if (!cacheValid) { //无效就把读锁释放,上写锁 //这个时候有可能有其他线程抢先获取了写锁 rwl.readLock().unlock();
            rwl.writeLock().lock(); try { //如果缓存还是无效 if (!cacheValid) { //更新data,缓存设为有效 data = new Object();
                    cacheValid = true;
                } //因为我们想要在后续打印出data,所以请求读锁 rwl.readLock().lock();
            } finally { //确保写锁释放,整个时候就是读锁了,然后打印data rwl.writeLock().unlock();
            }
        } try {
            System.out.println(data);
        } finally { //确保读锁能释放 rwl.readLock().unlock();
        }
    }
}

为什么需要锁降级?

如果我们一直使用写锁最后释放写锁,虽然线程安全,但是没必要,因为我们只有一处修改数据的代码:

后面data只是读,这个时候还用写锁就不能多个线程读了,持有写锁浪费资源,降低效率,这个时候就用锁的降级提高整体效率

ReentrantReadWriteLock支持锁降级,不支持锁升级

import java.util.concurrent.locks.ReentrantReadWriteLock; public class CachedData { final static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); public static void main(String[] args) {
        upgrade();
    } public static void upgrade() { //获取读锁 rwl.readLock().lock(); //获取写锁,发生阻塞 rwl.writeLock().lock(); //不会打印 System.out.println("成功升级");
    }
}
rwl.writeLock().lock(); rwl.readLock().lock(); rwl.readLock().unlock(); System.out.println("成功降级");

由此可见ReentrantReadWriteLock支持锁降级,不支持锁升级

为什么不支持?

写锁,只能有一个线程持有,并且不可能存在读锁和写锁同时持有的情况

不然A升级,B升级,大家一起升级,但是写锁只有一个,给谁?

那只能这样,比如A,B,C都持有读锁,A尝试升级到写锁,那么B,C就必须释放读锁,这个时候A可以升级为写锁

那如果A,B都想升级为写锁,A等待其他线程释放,B等待其他线程释放,A等B,B等A,死锁

实现方案

保证每次只有一个线程升级那么线程是安全的,只不过最常见的ReentrantReadWriteLock不支持

#Java##程序员#
全部评论
感谢大佬分享的锁,学到了
点赞 回复 分享
发布于 2022-10-09 17:33 陕西

相关推荐

字节 飞书绩效团队 (n+2) * 15 + 1k * 12 + 1w
点赞 评论 收藏
分享
巧克力1:双选会不如教室宣讲会
点赞 评论 收藏
分享
1 1 评论
分享
牛客网
牛客企业服务