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

news2024/9/28 17:33:59

背景

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不支持

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/186582.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

打造智慧工地,低代码平台助力基建行业全链路数字化升级

编者按:基建行业数字化转型需求迫切,低代码平台有助于加快数字化转型速度,赋能建筑工程企业升级。本文分析了低代码在基建行业中的应用价值,并指出基建行业对于低代码平台的需求,最后通过相关案例的展示了低代码平台在…

低代码平台解读:“低代码+PaaS”的技术创新实践

数字化转型已经成为必然趋势,几乎所有传统行业都喊出了数字化转型的口号。但在数字化转型中,很多企业面临着成本高、周期长的难题。低代码是其中一种破解难题的方式,如今的低代码已经是企业数字化的核心引擎。 低代码平台越来越多&#xff0…

数据结构——链表(五)

数据结构 文章目录数据结构前言一、什么是链表二、实现单链表1.单链表的结构2.单链表的初始化3.单链表的插入4.遍历链表5.链表长度总结前言 接下来学习一下链表,链表比数组用的更多。 一、什么是链表 概念:链表是一种物理存储结构上非连续、非顺序的存…

《从0开始学大数据》之如何自己开发一个大数据SQL引擎

背景 大数据仓库 Hive,作为一个成功的大数据仓库,它将 SQL 语句转换成 MapReduce 执行过程,并把大数据应用的门槛下降到普通数据分析师和工程师就可以很快上手的地步。 但是 Hive 也有自己的问题,由于它使用自己定义的 Hive QL …

Linux常用命令——repquota命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) repquota 报表的格式输出磁盘空间限制的状态 补充说明 repquota命令以报表的格式输出指定分区,或者文件系统的磁盘配额信息。 语法 repquota(选项)(参数)选项 -a:列出在/etc/fstab文…

gRPC 基础(二)-- Go 语言版 gRPC-go

gRPC-Go Github gRPC的Go实现:一个高性能、开源、通用的RPC框架,将移动和HTTP/2放在首位。有关更多信息,请参阅Go gRPC文档,或直接进入快速入门。 一、快速入门 本指南通过一个简单的工作示例让您开始在Go中使用gRPC。 1.1 先决条件 Go:…

word排版技巧:这几种特殊版式轻松搞定

我们在许多报刊、杂志中会见到一些带有特殊效果的文档,如首字下沉、分栏排版、竖排文档等形式的排版效果。这些效果其实不是只有专业排版软才能实现,利用Word我们可以轻松完成这些排版效果。1、首字下沉首字下沉是一种突出显示段落中的第一个汉字的排版方…

3小时快速上手sharding-jdbc

今日目标 1、理解分库分表基础概念【垂直分库分表、水平分库分表】 2、能够说出sharding-jdbc为我们解决什么问题 3、理解sharding-jdbc中的关键名词 4、理解sharding-jdbc的整体架构及原理 5、掌握sharding-jdbc的集成方式1.分库分表介绍 1.1 分库分表概述 分库分表就是为了…

普元EOS_工作流引擎相关数据表记录---工作流工作笔记002

由于现在在用普元的工作流引擎,但是,说明文档中没有对数据表的说明 所以整理一下记录下来: 业务流程(com.eos.workflow.data.WFProcessDefine) 属性 名称 类型 processDefID 业务流程ID long processDefName 业务流程名称 String processChName 业务流程显示名称 String desc…

计算机图形学 第5章 二维变换与裁剪完结

目录中点分割直线段裁剪算法中点计算公式代码Liang-Barsky直线段裁剪算法⭐⭐⭐代码:多边形裁剪算法/Sutherland-Hodgman裁剪算法代码相关学习资料:中点分割直线段裁剪算法 中点分割直线段裁剪算法对Cohen-Sutherland直线裁剪算法的第3种情况做了改进&a…

Centos 安装部署 Sentinel

在微服务架构中,业务高峰时段,请求过多可能导致直接查询数据库,造成雪崩等事故。 一、雪崩问题 微服务调用链路中某个服务故障,引起整个链路中所有服务不可用。 解决方案 1)超时处理 设置超时时间,请求超…

最近超火的ChatGPT到底怎么样?体验完后我有哪些感受和思考?

✔️本文主题:ChatGPT 人工智能 ✔️官方网站:chat.openai.com 文章目录前言二、初识三、深入四、编程相关编写纠错五、感想六、展望七、结语前言 大家好,这次我们来聊一聊最近超级火的人工智能语音——ChatGPT! ChatGPT是什么&…

从 synchronized 到 CAS 和 AQS的超详细解析

文章目录一、Synchronized 关键字二、悲观锁和乐观锁三、公平锁和非公平锁四、可重入锁和不可重入锁五、CAS5.1、操作模型5.2、重试机制(循环 CAS)5.3、底层实现5.4、ABA 问题六、可重入锁 ReentrantLock七、AQS7.1、请求锁7.2、创建 Node 节点并加入链表…

Vue3组合式API

Vue3组合式APIcomposition API 和 options API体验 composition APIsetup 函数reactive 函数ref 函数script setup语法composition API 和 options API vue2 采用的就是 optionsAPI (1) 优点:易于学习和使用, 每个代码有着明确的位置 (例如: 数据放 data 中, 方法放 methods中)…

Mac 打开JD-GUI报错:ERROR launching ‘JD-GUI‘

目录一、JD-GUI下载二、JD-GUI报错信息三、解决方案1、查找JD-GUI包内容2、修改universalJavaApplicationStub.sh文件一、JD-GUI下载 JD-GUI下载地址:https://github.com/java-decompiler/jd-gui/releases 二、JD-GUI报错信息 Mac系统版本:11.3 JD-GUI…

智能车|自主导航 ROS Navigation Stack 功能包 简介与编译

智能车|自主导航 ROS Navigation Stack 功能包 简介与编译前言功能包下载与编译前言 ros功能包:Navigation ros wiki:http://wiki.ros.org/navigation github 地址:https://github.com/ros-planning/navigation 功能包简介: ROS…

第一次创业,注册什么类型的公司?

前言 几乎每一个打工者都有一颗当老板的心,大喊一声:"大丈夫生居天地间,岂能郁郁久居人下",于是一拍桌子就辞职创业,现实往往都是潦草收场,看下面一段统计数据: 中国小微企业平均存活周期4.13年&…

通过Bypass UAC进行权限提升

什么是UAC用户账户控制(User Account control,UAC)是windows系统采用的一种控制机制,可以阻止自动安装未经授权的应用 并防止意外更改系统设置,有助于防止恶意软件损坏计算机。用户账户控制程序使应用程序和任务始终在…

2.5.3 PCIe——物理电气子层——动态均衡

因为PCIE 3.0信号的速率可以达到8Gb/s,而且链路通道走线也可能会很长,这可能会导致高速信号衰减过大,为了补偿channel的衰减需要增加传输信号的高频成分,让高频和低频能量差不多,这就是equalization。因此在PCIE 3.0的…

DNS Sec

域名系统(Domain Name System,DNS)响应消息中给出域名服务器的IP地址、完全合格的域名与IP地址之间的绑定关系等,因此,DNS响应消息的真实性和完整性直接关系用户访问网络过程的安全性。为了保证DNS响应消息的真实性和完…