报警规范管理

news2024/9/21 14:30:40

报警规则管理

想要获取报警数据,我们首先必须先制定报警规则,会根据不同的设备,不同的物模型来定义报警规则

需求分析

我们先来分析需求,打开原型图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据来源:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

逻辑规则:

1)若多条报警规则是包含/互斥关系时,只要符合报警规则时,就会产生一条报警数据;

例如:规则1: 手表电量 >=10 ,规则2:手表电量< 50, 此时是包含关系,当手机电量=40时,符合两条报警规则,则产生两条报警数据;

2)报警数据见下方示例,1分钟(数据聚合周期)检查一次智能手表(所属产品)中的全部设备(关联设备)的血氧(功能名称),

监控值(统计字段)是否 < 90(运算符+阈值),当持续3个周期(持续周期)都满足这个规则时,触发报警;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通知方式

1)报警生效时间:报警规则的生效时间,报警规则只在生效时间内才会检查监控数据是否需要报警;

2)报警沉默周期:指报警发生后如果未恢复正常,重复发送报警通知的时间间隔;

报警方式:

1)当触发报警规则时,则发送消息通知,

通知对象:设备数据类型=老人异常数据时,通知老人对应的护理员;设备数据类型=设备异常数据时,通知后勤部维修工;

渠道:站内信(见消息通知模块原型图)、短信;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

基于前面的报警规则,一旦有不符合预期的数据就会触发报警,产生报警数据,通知相关的负责人解决。

这里的概念大家要搞清楚,才能梳理清楚报警数据的过滤流程。下面举个例子来说明一下

案例一

详细报警规则,如下图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

监测的产品为睡眠监测带,物模型为心率,过滤的是该产品下的所有设备

报警类型为老人异常数据(设备报警通知老人绑定的护理员和超级管理员

持续周期:

  • 持续1个周期(1周期=1分钟):表示触发报警之后,马上会保存报警数据
  • 持续3个周期(1周期=1分钟):表示触发报警之后,连续三次都是异常数据才会保存报警数据

阈值为65,运算符为**<:表示采集的心率数据如果小于65**就触发报警

沉默周期为5分钟,已经保存报警数据之后,如果后面有连续报警,5分钟之后再触发报警规则

报警生效时段为00:00:00~23:59:59:表示任意时段都会采集数据

案例二

报警规则如下图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

监测的产品为烟雾报警器,物模型为**温度,**过滤的是该产品下的全部设备

报警类型为设备异常数据(设备报警通知行政和超级管理员

持续周期为持续1个周期(1后期=1分钟):表示触发报警之后,马上会保存报警数据

阈值为55,运算符为**>=:表示采集的室内温度数据大于等于55**就触发报警

沉默周期为5分钟,已经保存报警数据之后,如果后面有连续报警,5分钟之后再触发报警规则

报警生效时段为00:00:00~23:59:59:表示任意时段都会采集数据

添加规则

AlertRuleController

package com.zzyl.controller.web;

import com.zzyl.base.ResponseResult;
import com.zzyl.controller.BaseController;
import com.zzyl.entity.AlertRule;
import com.zzyl.service.AlertRuleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AlertRuleController extends BaseController {

    @Autowired
    private AlertRuleService alertRuleService;

    // 创建告警规则
    @PostMapping("/alert-rule/create")
    public ResponseResult createAlertRule(@RequestBody AlertRule alertRule) {
        alertRuleService.save(alertRule);
        return success();
    }
}

查询规则

AlertRuleController

    //查询报警规则
    @GetMapping("/alert-rule/get-page")
    public ResponseResult getAlertRulePage(Integer pageNum, Integer pageSize, String alertRuleName, String productKey, String functionName) {
        PageResponse<AlertRuleVo> pageResponse = alertRuleService.getAlertRulePage(pageNum, pageSize, alertRuleName, productKey, functionName);
        return success(pageResponse);
    }

AlertRuleService

    PageResponse<AlertRuleVo> getAlertRulePage(Integer pageNum, Integer pageSize, String alertRuleName, String productKey, String functionName);

AlertRuleServiceImpl

    @Override
    public PageResponse<AlertRuleVo> getAlertRulePage(Integer pageNum, Integer pageSize, String alertRuleName, String productKey, String functionName) {

        Page<AlertRule> page = new Page(pageNum, pageSize);

        LambdaQueryWrapper<AlertRule> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StrUtil.isNotEmpty(alertRuleName), AlertRule::getAlertRuleName, alertRuleName)
                .eq(StrUtil.isNotEmpty(productKey), AlertRule::getProductKey, productKey)
                .eq(StrUtil.isNotEmpty(functionName), AlertRule::getFunctionName, functionName);
        page = getBaseMapper().selectPage(page, wrapper);

        List<AlertRuleVo> list = page.getRecords().stream().map(v -> {
            AlertRuleVo alertRuleVo = BeanUtil.copyProperties(v, AlertRuleVo.class);
            alertRuleVo.setRules(new StringBuilder(v.getFunctionName()).append(v.getOperator()).append(v.getValue()).append("持续触发").append(v.getDuration()).append("个周期时发生报警").toString());
            return alertRuleVo;
        }).collect(Collectors.toList());

        return new PageResponse(page, list);
    }

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

报警功能实现

报警数据采集

思路分析

基于我们刚才创建的规则,当设备上报数据的时候,我们就需要进行过滤,详细流程如下:

https://heuqqdmbyk.feishu.cn/wiki/NplEwj6NiiOntwkBo1KcGomKnnh

AlertTask

在zzyl-service中新增定时任务类,启动数据过滤

package com.zzyl.task;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.zzyl.entity.AlertRule;
import com.zzyl.service.AlertRuleService;
import com.zzyl.vo.DeviceDataVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

//设备上报数据报警规则过滤
@Component
public class AlertTask {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private AlertRuleService alertRuleService;

    /**
     * 设备上报数据报警规则过滤<br>
     * 周期:每分钟执行一次
     */
    @Scheduled(cron = "0 * * * * ? ")
    public void deviceDataAlertFilter() {
        //1.查询所有报警规则,如果为空,程序结束
        List<AlertRule> allAlertRules = alertRuleService.list();
        if (CollUtil.isEmpty(allAlertRules)) {
            return;
        }

        //2.查询所有设备最新上报数据,如果为空,程序结束
        List<Object> jsonStrList = redisTemplate.opsForHash().values("DEVICE_LAST_DATA");
        if (CollUtil.isEmpty(jsonStrList)) {
            return;
        }

        //3.设备上报数据不为空,提取设备上报数据为list
        List<DeviceDataVo> deviceDataVoList = new ArrayList<>();
        jsonStrList.forEach(json -> deviceDataVoList.addAll(JSONUtil.toList(json.toString(), DeviceDataVo.class)));

        //4.对每一条设备上报数据进行报警规则校验
        deviceDataVoList.forEach(d -> alertRuleService.alertFilter(d));
    }
}

AlertRuleService

//校验设备上报数据,进行报警规则过滤处理
void alertFilter(DeviceDataVo deviceDataVo);

AlertRuleServiceImpl

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private AlertDataService alertsDataService;

    @Autowired
    private UserMapper userMapper;

    //设备维护人员的角色名称
    @Value("${zzyl.alert.deviceMaintainerRole}")
    private String deviceMaintainerRole;
    //超级管理员的角色名称
    @Value("${zzyl.alert.managerRole}")
    private String managerRole;


    /**
     * 校验设备上报数据,进行报警规则过滤处理
     *
     * @param deviceDataVo 设备上报数据
     */
    @Override
    public void alertFilter(DeviceDataVo deviceDataVo) {
        //1. 获取设备上报数据时间,如果上报发生在1分钟前(1分钟前的数据应该已经被前一次定时任务处理过),不再处理
        LocalDateTime alarmTime = deviceDataVo.getAlarmTime();
        long between = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now(), ChronoUnit.SECONDS);
        if (between > 60) {
            return;
        }

        //2. 查询应用在当前设备(有专门针对于当前设备的,还有全部设备的)当前功能上的所有规则
        LambdaQueryWrapper<AlertRule> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AlertRule::getFunctionId, deviceDataVo.getFunctionId())
                .eq(AlertRule::getProductKey, deviceDataVo.getProductKey())
                .in(AlertRule::getIotId, deviceDataVo.getIotId(), "-1");
        List<AlertRule> alertRules = getBaseMapper().selectList(wrapper);
        //如果报警规则为空,程序结束
        if (CollectionUtil.isEmpty(alertRules)) {
            return;
        }

        //如果报警规则不为空,遍历报警规则,进行校验
        alertRules.forEach(alertRule -> deviceDateAlarmHandler(deviceDataVo, alertRule));
    }

    /**
     * 设备数据报警处理器
     *
     * @param deviceDataVo 设备上报数据
     * @param alertRule    报警规则
     */
    private void deviceDateAlarmHandler(DeviceDataVo deviceDataVo, AlertRule alertRule) {
        //第一层校验:判断是否在生效时段内
        String[] aepArr = alertRule.getAlertEffectivePeriod().split("~");
        LocalTime startTime = LocalTime.parse(aepArr[0]);
        LocalTime endTime = LocalTime.parse(aepArr[1]);
        LocalTime time = LocalDateTimeUtil.of(deviceDataVo.getAlarmTime()).toLocalTime();//上报时间
        //如果上报时间不在生效时间段内,校验结束
        if (startTime.isAfter(time) || endTime.isBefore(time)) {
            return;
        }

        //设备id
        String iotId = deviceDataVo.getIotId();
        //从redis中获取数据,报警规则连续触发次数rediskey
        String aggCountKey = "alert_trigger_count:" + iotId + ":" + deviceDataVo.getFunctionId() + ":" + alertRule.getId();

        //第二层校验:将设备上报值和阈值比较
        //x – 第一个值,y – 第二个值。x==y返回0,x<y返回小于0的数,x>y返回大于0的数
        int compareResult = NumberUtil.compare(Double.parseDouble(deviceDataVo.getDataValue()), alertRule.getValue());
        //或 逻辑运算,2个条件满足其一即为true
        //1.运算符为>=,且设备上报值 >= 阈值
        //2.运算符为<,且设备上报值 < 阈值
        if ((ObjectUtil.equals(alertRule.getOperator(), ">=") && compareResult >= 0)
                || (ObjectUtil.equals(alertRule.getOperator(), "<") && compareResult < 0)) {
            log.debug("此时设备上报数据达到阈值");
        } else {
            //该情况不符合报警规则,所以设备上报数据为正常数据。需要删除redis聚合的异常数据,程序结束
            redisTemplate.delete(aggCountKey);
            return;
        }

        //第三层校验:校验当前设备+功能的报警规则是否处于沉默周期
        String silentCacheKey = "alert_silent:" + iotId + ":" + deviceDataVo.getFunctionId() + ":" + alertRule.getId();
        String silentData = redisTemplate.opsForValue().get(silentCacheKey);
        //如果redis中存在对应沉默周期数据,则当前设备+功能正处于沉默周期,无需报警
        if (StrUtil.isNotEmpty(silentData)) {
            return;
        }

        //第四层校验:校验持续周期
        //4.1获取连续触发报警规则的次数
        //aggCountData是上次触发报警累计到的次数,这一次又触发了报警,所以累积到这次就+1
        String aggCountData = redisTemplate.opsForValue().get(aggCountKey);
        int triggerCount = ObjectUtil.isEmpty(aggCountData) ? 1 : Integer.parseInt(aggCountData) + 1;

        //4.2判断次数是否与持续周期相等
        //如果触发报警规则次数不等于持续周期,则无需报警
        if (ObjectUtil.notEqual(alertRule.getDuration(), triggerCount)) {
            redisTemplate.opsForValue().set(aggCountKey, triggerCount + "");
            return;
        }

        //如果触发报警规则次数等于持续周期,则需要报警,完成以下操作:
        //1)删除报警规则连续触发次数的缓存
        //2)添加对应的沉默周期,设置过期时间添加对应的沉默周期,设置过期时间
        //3)报警数据存储到数据库
        redisTemplate.delete(aggCountKey);
        redisTemplate.opsForValue().set(silentCacheKey, deviceDataVo.getDataValue(), alertRule.getAlertSilentPeriod(), TimeUnit.MINUTES);

        //获取消息的消费者
        List<Long> consumerIds = null;
        if (ObjectUtil.equals(0, alertRule.getAlertDataType())) {
            //如果是老人报警数据,需要通知绑定老人的护理员。根据iotId查询老人绑定的护理员
            if (deviceDataVo.getLocationType() == 0) {
                consumerIds = deviceMapper.selectNursingIdsByIotIdWithElder(iotId);
            } else if (deviceDataVo.getLocationType() == 1 && deviceDataVo.getPhysicalLocationType() == 2) {
                consumerIds = deviceMapper.selectNursingIdsByIotIdWithBed(iotId);
            }
        } else {
            //如果是设备报警数据,需要通知设备维护人员。根据指定角色名称查询相关用户
            consumerIds = userMapper.selectUserIdsByRoleName(deviceMaintainerRole);
        }
        //查询超级管理员,超级管理员无论什么消息都会接收
        List<Long> managerIds = userMapper.selectUserIdsByRoleName(managerRole);
        List<Long> allConsumerIds = CollUtil.addAllIfNotContains(consumerIds, managerIds);
        allConsumerIds = CollUtil.distinct(allConsumerIds);

        //新增报警数据
        insertAlertData(deviceDataVo, alertRule, allConsumerIds);
    }

    //新增报警数据
    private void insertAlertData(DeviceDataVo deviceDataVo, AlertRule alertRule, List<Long> consumerIds) {
        String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警", alertRule.getFunctionName(), alertRule.getOperator(), alertRule.getValue(), alertRule.getDuration());
        AlertData alertData = BeanUtil.toBean(deviceDataVo, AlertData.class);
        alertData.setAlertRuleId(alertRule.getId());
        alertData.setAlertReason(alertReason);
        alertData.setType(alertRule.getAlertDataType());
        alertData.setStatus(0);

        List<AlertData> list = consumerIds.stream().map(id -> {
            AlertData dbAlertData = BeanUtil.toBean(alertData, AlertData.class);
            dbAlertData.setUserId(id);
            return dbAlertData;
        }).collect(Collectors.toList());
        alertsDataService.saveBatch(list);
    }

上述代码中,对于超级管理员和维修工是通过配置的方式读取的名字,需要在application.yml文件中添加数据

zzyl:
  alert:
    deviceMaintainerRole: 维修工
    managerRole: 超级管理员

DeviceMapper

    List<Long> selectNursingIdsByIotIdWithElder(String iotId);

    List<Long> selectNursingIdsByIotIdWithBed(String iotId);

DeviceMapper.xml

    <select id="selectNursingIdsByIotIdWithElder" resultType="java.lang.Long">
        SELECT ne.nursing_id
        FROM monitor_device AS d
                 LEFT JOIN elder AS e ON e.id = d.binding_location
                 LEFT JOIN nursing_elder AS ne ON ne.elder_id = e.id
        WHERE d.location_type = 0
          AND d.iot_id = #{iotId}
    </select>

    <select id="selectNursingIdsByIotIdWithBed" resultType="java.lang.Long">
        SELECT ne.nursing_id
        FROM monitor_device AS d
                 left join base_bed b on d.binding_location = b.id
                 LEFT JOIN elder AS e ON e.bed_id = b.id
                 LEFT JOIN nursing_elder AS ne ON ne.elder_id = e.id
        where d.location_type = 1
          and d.physical_location_type = 2
          and d.iot_id = #{iotId}
    </select>

UserMapper

    @Select("select sur.user_id from sys_user_role sur left join sys_role sr on sur.role_id = sr.id where sr.role_name  = #{roleName}")
    List<Long> selectUserIdsByRoleName(String roleName);

测试

创建一条规则,如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

启动该设备的数据上报,手动改一下心率物模型的参数,需要保证上报的心率值地域65才行

启动后端项目

  • 查看redis中key:alert_trigger_count,查看统计数据的变化
  • 查看redis中key:alert_silent,沉默周期数据的变化
  • 如果已经触发了报警规则三次后,是否能保存数据到数据库中,查看通知人的个人,保存的数据的条数是否相同

数据报警提醒

智能床位

我们打开智能床位的原型图,如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 当床位的设备数据出现了报警数据之后,会有标红通知
  • 有报警的楼层,在tab选项卡中会出现红点,来提醒工作人员查看并处理

家属端异常数据展示

在家属端会展示,最新采集的异常数据,如下图展示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

站内信通知

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其他通知

  • 企业微信
  • 钉钉
  • 短信
  • 语音播报
  • 电话
  • 硬件

t,查看统计数据的变化

  • 查看redis中key:alert_silent,沉默周期数据的变化
  • 如果已经触发了报警规则三次后,是否能保存数据到数据库中,查看通知人的个人,保存的数据的条数是否相同

数据报警提醒

智能床位

我们打开智能床位的原型图,如下:

[外链图片转存中…(img-0z1WCNp9-1725175520072)]

  • 当床位的设备数据出现了报警数据之后,会有标红通知
  • 有报警的楼层,在tab选项卡中会出现红点,来提醒工作人员查看并处理

家属端异常数据展示

在家属端会展示,最新采集的异常数据,如下图展示

[外链图片转存中…(img-ghCcAmU4-1725175520072)]

站内信通知

[外链图片转存中…(img-w95nWWP7-1725175520072)]

其他通知

  • 企业微信
  • 钉钉
  • 短信
  • 语音播报
  • 电话
  • 硬件

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

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

相关文章

CSS-层叠上下文【看这一篇就够了!!!】

目录 前序 z-index设置定位元素层叠顺序 z-index值相同时&#xff0c;写在后面的覆盖写在前面的 z-index值越大&#xff0c;越在上面显示 z-index值为负数 CSS中的层叠上下文 什么是“层叠上下文” 层叠上下文的创建 根层叠上下文 定位元素的传统层叠上下文 层叠顺序…

15:发光二极管布局要求

1.指示灯一般放正面&#xff0c; 靠板边

【Android】Material Design编写更好的UI

Toolbar 对于控件ActionBar我们非常熟悉&#xff0c;就是我们常见的标题栏&#xff0c;但ActionBar只能位于活动的顶部&#xff0c;因此我们更建议使用Toolbar。在新建一个项目的时候都是默认显示ActionBar&#xff0c;我们要使用Toolbar就需要先将标题栏改为不显示 先来看看…

在Ubuntu上使用apt工具安装RabbitMQ

创建安装脚本 cd home/ madir scripts cd scripts 创建脚本前&#xff0c;需要确认Linux版本。不同的版本对应着不同的运行脚本。 lsb_release -a 查看Linux版本 可以看到&#xff0c;我的Ubuntu版本是22.04。 在这里找到对应的脚本复制。 创建脚本文件&#xff1a; ca…

详解树状数组(C/C++)

树状数组&#xff08;Binary Indexed Tree&#xff0c;简称BIT或Fenwick Tree&#xff09;是一种用于高效处理数据序列的算法数据结构。它能够支持两个主要操作&#xff1a;单点更新和区间求和&#xff0c;这两个操作的时间复杂度都能达到O(log n)&#xff0c;其中 n 是数据序列…

STM32基础篇:SPI片上外设

SPI外设简介 STM32芯片内部集成了SPI片上外设&#xff0c;可由硬件自动执行时钟生成、数据收发等功能&#xff0c;减轻CPU负担。对于STM32F103C8T6&#xff0c;其SPI资源有SPI1、SPI2。 一些参数配置&#xff1a; 8位/16位数据帧高位先行/低位先行时钟频率&#xff1a;PCLK/…

vue3本地运行错误集

1、解决报错ValidationError: Progress Plugin Invalid Options问题 ValidationError: Progress Plugin Invalid Optionsoptions should NOT have additional propertiesoptions should NOT have additional propertiesoptions should NOT have additional propertiesoptions …

SMART PLC 脉冲输出指令PLS应用

200SMART PLC如何实现可调频率可调占空比PWM输出 200smart_PLC如何实现可调频率可调占空比PWM输出_200smart pwm-CSDN博客文章浏览阅读6.4k次,点赞2次,收藏7次。本文介绍了如何在SMART PLC中通过修改原向导接口,实现可调频率和占空比的PWM输出。详细阐述了脉冲周期、占空比…

DMA简述与使用实例

之后要学&#xff1a;SPI / IICDMA 学习的这位up主的视频&#xff1a;全网最清楚的DMA讲解&#xff0c;三种搬运模式三个例子讲清楚&#xff08;STM32教程基于HAL库和CUBEIDE&#xff09;_哔哩哔哩_bilibili 目录 01-基本信息 1-概述 2-方向 3-模式 正常模式 轮询模式 …

学习日志8.30--防火墙NAT

目录 一、实验环境配置 二、配置防火墙静态NAT一对一 三、配置防火墙静态NAT多对多 四、配置防火墙NAT端口转换NAPT 五、防火墙smart-nat、easyip 六、防火墙三元组NAT 在学习过基于路由器的NAT网络地址转换&#xff0c;现在学习基于防火墙NAT的网络地址转换&#xff0c;…

python-读写Excel:xlwings库操作

几种操作Excel的python库对比 安装:pip install xlwings 目录 APP实例化对象 工作薄对象 创建工作薄 打开工作薄 工作薄属性 工作表对象 新增工作表 复制表 获取工作表对象 工作表属性 删除和清除表数据及表格式 工作表行高列宽(自动调整) 单元格对象 获取单元…

【hot100篇-python刷题记录】【旋转图像】

R7-矩阵篇 印象题&#xff1a; 思路&#xff1a; 先转置&#xff0c;转置完我们按照列的中间进行对称交换就可以了。 class Solution:def rotate(self, matrix: List[List[int]]) -> None:"""Do not return anything, modify matrix in-place instead.&qu…

【微机原理】指令JZ和JNZ的区别

&#x1f31f; 嗨&#xff0c;我是命运之光&#xff01; &#x1f30d; 2024&#xff0c;每日百字&#xff0c;记录时光&#xff0c;感谢有你一路同行。 &#x1f680; 携手启航&#xff0c;探索未知&#xff0c;激发潜能&#xff0c;每一步都意义非凡。 JZ&#xff08;Jump …

祝贺严建兵教授任华中农业大学校长

公众号&#xff1a;生信漫谈&#xff0c;获取最新科研信息&#xff01; 祝贺严建兵教授任华中农业大学校长https://mp.weixin.qq.com/s?__bizMzkwNjQyNTUwMw&mid2247487040&idx1&sn6800055c9944754be11dc77a30ee1906&chksmc0e9ebb0f79e62a64634d5cd057578ca5…

Java 入门指南:Java 并发编程 —— AQS、AQLS、AOS 锁与同步器的框架

AQS AQS 是 AbstractQueuedSynchronizer 的缩写&#xff0c;即 抽象队列同步器&#xff0c;是 Java.util.concurrent 中的一个基础工具类&#xff0c;用于实现同步器&#xff08;Synchronizer&#xff09;的开发。 AQS 提供了一种实现锁和同步器的框架&#xff0c;使用 AQS 能…

Mysql高级篇(上)

Mysql高级篇&#xff08;上&#xff09; Mysql架构介绍(一)1、Linux环境下的MySQL的安装与使用2、MySQL请求到响应字符集变化&#xff08;了解&#xff09;3、MySQL8 的主要目录结构4、数据库和文件系统关系&#xff08;1&#xff09;默认数据库&#xff08;2&#xff09;数据库…

C语言程序设计之基础易错题锦集6

C语言程序设计之基础易错题锦集6 问题 6_0解析 6_0 问题 6_0 将形参 s 所指字符串中字母字符顺序前移&#xff0c;其他字符顺序后移&#xff0c;处理后将新字符串的首地址作为函数值返回。 例如&#xff1a;输入 &#xff1a;“asd123fgh456df”, 输出&#xff1a;“a…

React基础面试题

React 面试题 以下是面试官最有可能问到的 50 个 React 面试题和答案。为方便你学习&#xff0c;我对它们进行了分类&#xff1a; 基本知识React 组件React ReduxReact 路由 基本知识 1. 区分Real DOM和Virtual DOM Real DOMVirtual DOM1. 更新缓慢。1. 更新更快。2. 可以…

简化理解:Tomcat 和 Servlet 规范

有时候&#xff0c;我们会把复杂的技术概念弄得很复杂&#xff0c;其实这些东西可以用更简单的语言来理解。我们来看看 Tomcat 和 Servlet 规范到底是怎么回事。 1. 什么是 Servlet 规范&#xff1f; 简单来说&#xff0c;Sun 公司&#xff08;现在是 Oracle&#xff09;定了…

YOLOv9改进策略【模型轻量化】| MoblieNetV3:基于搜索技术和新颖架构设计的轻量型网络模型

一、本文介绍 本文记录的是基于MobileNet V3的YOLOv9目标检测轻量化改进方法研究。MobileNet V3的模型结构是通过网络搜索得来的&#xff0c;其中的基础模块结合了MobileNet V1的深度可分离卷积、MobileNet V2的线性瓶颈和倒置残差结构以及MnasNet中基于挤压和激励的轻量级注意…