06 Redis分布式锁

news2025/3/1 0:26:32

常见面试问题

  • Redis除了拿来做缓存,你还见过基于Redis的什么用法?
  • Redis 做分布式锁的时候有需要注意的问题?
  • 如果是 Redis 是单点部署的,会带来什么问题?那你准备怎么解决单点问题呢?
  • 集群模式下,比如主从模式,有没有什么问题呢?
  • 你知道 Redis 是怎么解决集群模式也不靠谱的问题的吗?
  • 那你简单的介绍一下 Redlock 吧?你简历上写redisson,你谈谈
  • 你觉得 Redlock 有什么问题呢?
  • Redis分布式锁如何续期?看门狗知道吗?

锁的分类

  • 单机版同一个JVM虚拟机内,synchronized或者Lock接口
  • 分布式不同个JVM虚拟机内,单机的线程锁机制不再起作用,资源类在不同的服务器之间共享了。

一个靠谱分布式锁需要具备的条件和刚需

  1. 独占性:OnlyOne,任何时刻只能有且仅有一个线程持有
  2. 高可用:若redis集群环境下,不能因为某一个节点挂了而出现获取锁和释放锁失败的情况
  3. 防死锁:杜绝死锁,必须有超时控制机制或者撤销操作,有个兜底终止跳出方案
  4. 不乱抢:防止张冠李戴,不能私下unlock别人的锁,只能自己加锁自己释放。
  5. 重入性:同一个节点的同一个线程如果获得锁之后,它也可以再次获取这个锁。

分布式锁

在这里插入图片描述

setnx key value

在这里插入图片描述
差评,setnx+expire不安全,两条命令非原子性的

Base案例(boot+redis)

  • 使用场景:多个服务间保证同一时刻同一时间段内同一用户只能有一个请求(防止关键业务出现并发攻击)

  • 建Module

    • boot_redis01
    • boot_redis02
  • 改POM

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.10.RELEASE</version>
            <relativePath/>
        </parent>
    
        <groupId>com.atguigu.redis</groupId>
        <artifactId>boot_redis01</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <!--guava-->
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>23.0</version>
            </dependency>
            <!--web+actuator-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <!--SpringBoot与Redis整合依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-pool2</artifactId>
            </dependency>
            <!-- jedis -->
            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
                <version>3.1.0</version>
            </dependency>
            <!-- springboot-aop 技术-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
            <!-- redisson -->
            <dependency>
                <groupId>org.redisson</groupId>
                <artifactId>redisson</artifactId>
                <version>3.13.4</version>
            </dependency>
            <!--一般通用基础配置-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    
  • 写YML

    server.port=1111
    
    # ========================redis相关配置=====================
    # Redis数据库索引(默认为0)
    spring.redis.database=0  
    # Redis服务器地址
    spring.redis.host=127.0.0.1
    # Redis服务器连接端口
    spring.redis.port=6379  
    # Redis服务器连接密码(默认为空)
    spring.redis.password=
    # 连接池最大连接数(使用负值表示没有限制) 默认 8
    spring.redis.lettuce.pool.max-active=8
    # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
    spring.redis.lettuce.pool.max-wait=-1
    # 连接池中的最大空闲连接 默认 8
    spring.redis.lettuce.pool.max-idle=8
    # 连接池中的最小空闲连接 默认 0
    spring.redis.lettuce.pool.min-idle=0
    
  • 业务类

    • config
      package com.learn.config;
      
      import org.redisson.Redisson;
      import org.redisson.config.Config;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
      import org.springframework.data.redis.core.RedisTemplate;
      import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
      import org.springframework.data.redis.serializer.StringRedisSerializer;
      
      import java.io.Serializable;
      
      /**
       * @author YSK
       * @since 2023/6/2 15:15
       */
      @Configuration
      public class RedisConfig {
          @Bean
          public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory connectionFactory) {
              RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
              redisTemplate.setConnectionFactory(connectionFactory);
      
              redisTemplate.setKeySerializer(new StringRedisSerializer());
              redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
      
              return redisTemplate;
          }
      
          @Bean
          public Redisson redisson() {
              Config config = new Config();
      
              config.useSingleServer().setAddress("redis://127.0.0.1:6379").setDatabase(0);
      
              return (Redisson) Redisson.create(config);
          }
      }
      
    • controller
      package com.learn.controller;
      
      import org.redisson.Redisson;
      import org.redisson.api.RLock;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.data.redis.core.StringRedisTemplate;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      /**
       * @author YSK
       * @since 2023/6/2 15:16
       */
      @RestController
      public class GoodController {
          @Autowired
          private StringRedisTemplate stringRedisTemplate;
          public static final String KEY = "atguiguLock_0511";
      
          @Value("${server.port}")
          private String serverPort;
          @Autowired
          private Redisson redisson;
      
          @GetMapping("/buy_goods")
          public String buy_Goods()
          {
              String result = stringRedisTemplate.opsForValue().get("goods:001");
              int goodsNumber = result == null ? 0 : Integer.parseInt(result);
      
              if(goodsNumber > 0)
              {
                  int realNumber = goodsNumber - 1;
                  stringRedisTemplate.opsForValue().set("goods:001",realNumber + "");
                  System.out.println("你已经成功秒杀商品,此时还剩余:" + realNumber + "件"+"\t 服务器端口:"+serverPort);
                  return "你已经成功秒杀商品,此时还剩余:" + realNumber + "件"+"\t 服务器端口:"+serverPort;
              }else{
                  System.out.println("商品已经售罄/活动结束/调用超时,欢迎下次光临"+"\t 服务器端口:"+serverPort);
              }
      
              return "商品已经售罄/活动结束/调用超时,欢迎下次光临"+"\t 服务器端口:"+serverPort;
          }
      }
      
      

在这里插入图片描述

上述案例代码存在的问题

单机版没加锁

  • 没有加锁,并发下数字不对,出现超卖现象
  • 思考
    • 加synchronized
    • 加ReentrantLock
    • 都可以

解决

@RestController
public class GoodController
{
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/buy_goods")
    public String buy_Goods()
    {
        synchronized (this) {
            String result = stringRedisTemplate.opsForValue().get("goods:001");
            int goodsNumber = result == null ? 0 : Integer.parseInt(result);

            if(goodsNumber > 0)
            {
                int realNumber = goodsNumber - 1;
                stringRedisTemplate.opsForValue().set("goods:001",realNumber + "");
                System.out.println("你已经成功秒杀商品,此时还剩余:" + realNumber + "件"+"\t 服务器端口:"+serverPort);
                return "你已经成功秒杀商品,此时还剩余:" + realNumber + "件"+"\t 服务器端口:"+serverPort;
            }else{
                System.out.println("商品已经售罄/活动结束/调用超时,欢迎下次光临"+"\t 服务器端口:"+serverPort);
            }

            return "商品已经售罄/活动结束/调用超时,欢迎下次光临"+"\t 服务器端口:"+serverPort;
        }
    }
}

解释

  • 在单机环境下,可以使用synchronized或Lock来实现。
  • 但是在分布式系统中,因为竞争的线程可能不在同一个节点上(同一个jvm中),所以需要一个让所有进程都能访问到的锁来实现(比如redis或者zookeeper来构建)
  • 不同进程jvm层面的锁就不管用了,那么可以利用第三方的一个组件,来获取锁,未获取到锁,则阻塞当前想要运行的线程

nginx分布式微服务架构

问题

分布式部署后,单机锁还是出现超卖现象,需要分布式锁

Nginx配置负载均衡

jmeter压测
在这里插入图片描述

解决

  • 上redis分布式锁setnx
  • 在这里插入图片描述
    @GetMapping("/buy_goods")
    public String buy_Goods()
    {
        String key = "zzyyRedisLock";
        String value = UUID.randomUUID().toString()+Thread.currentThread().getName();

        Boolean flagLock = stringRedisTemplate.opsForValue().setIfAbsent(key, value);
        if(!flagLock)
        {
            return "抢夺锁失败,o(╥﹏╥)o";
        }

        String result = stringRedisTemplate.opsForValue().get("goods:001");
        int goodsNumber = result == null ? 0 : Integer.parseInt(result);

        if(goodsNumber > 0)
        {
            int realNumber = goodsNumber - 1;
            stringRedisTemplate.opsForValue().set("goods:001",realNumber + "");
            stringRedisTemplate.delete(key);
            System.out.println("你已经成功秒杀商品,此时还剩余:" + realNumber + "件"+"\t 服务器端口:"+serverPort);
            return "你已经成功秒杀商品,此时还剩余:" + realNumber + "件"+"\t 服务器端口:"+serverPort;
        }else{
            System.out.println("商品已经售罄/活动结束/调用超时,欢迎下次光临"+"\t 服务器端口:"+serverPort);
        }

        return "商品已经售罄/活动结束/调用超时,欢迎下次光临"+"\t 服务器端口:"+serverPort;


    }

finally必须关闭锁资源

  • 出异常的话,可能无法释放锁,必须要在代码层面finally释放锁
  • 加锁解锁,lock/unlock必须同时出现并保证调用
    在这里插入图片描述

宕机问题

  • 部署了微服务jar包的机器挂了,代码层面根本没有走到finally这块,没办法保证解锁,这个key没有被删除,需要加入一个过期时间限定key
  • 在这里插入图片描述

设置key+过期时间分开了,必须要合并成一行具备原子性

在这里插入图片描述

删除了别人的锁

在这里插入图片描述

  • 只能自己删除自己的,不许动别人的
  • 在这里插入图片描述

finally块的判断+del删除操作不是原子性的

  • 用Lua脚本
  • Redis调用Lua脚本通过eval命令保证代码执行的原子性

RedisUtils

 
package com.zzyy.study.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import javax.annotation.PostConstruct;

/**
 * @auther zzyy
 * @create 2020-09-20 16:44
 */
public class RedisUtils
{
    private static JedisPool jedisPool;

    static {
        JedisPoolConfig jedisPoolConfig=new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(20);
        jedisPoolConfig.setMaxIdle(10);
        jedisPool=new JedisPool(jedisPoolConfig,"192.168.111.147",6379);
    }

    public static Jedis getJedis() throws Exception {
        if(null!=jedisPool){
            return jedisPool.getResource();
        }
        throw new Exception("Jedispool was not init");
    }

}

在这里插入图片描述

对比Zookeeper,重点

  • Redis单机是CP集群是AP
  • Redis集群:redis异步复制造成的锁丢失,比如:主节点没来的及把刚刚set进来这条数据给从节点,master就挂了,从机上位但从机上无该数据

总结

RedisConfig

package com.learn.config;

import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.io.Serializable;

/**
 * @author YSK
 * @since 2023/6/2 15:15
 */
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory connectionFactory) {
        RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);

        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        return redisTemplate;
    }

    @Bean
    public Redisson redisson() {
        Config config = new Config();

        config.useSingleServer().setAddress("redis://127.0.0.1:6379").setDatabase(0);

        return (Redisson) Redisson.create(config);
    }
}

在这里插入图片描述
在这里插入图片描述

  • synchronized单机版OK,上分布式
  • nginx分布式微服务,单机锁不行/(ㄒoㄒ)/~~
  • 取消单机锁,上redis分布式锁setnx
  • 只加了锁,没有释放锁,出异常的话,可能无法释放锁,必须要在代码层面finally释放锁
  • 宕机了,部署了微服务代码层面根本没有走到finally这块,没办法保证解锁,这个key没有被删除,需要有lockKey的过期时间设定
  • 为redis的分布式锁key,增加过期时间。此外,还必须要setnx+过期时间必须同一行
  • 必须规定只能自己删除自己的锁,你不能把别人的锁删除了,防止张冠李戴,1删2,2删3
  • redis集群环境下,我们自己写的也不OK。直接上RedLock之Redisson落地实现

Redis分布式锁-Redlock算法

  • 使用场景:多个服务间保证同一时刻同一时间段内同一用户只能有一个请求(防止关键业务出现并发攻击)
  • Redis分布式锁比较正确的姿势是采用redisson这个客户端工具

单机案例

  • 三个重要元素
    • 加锁:加锁实际上就是在redis中,给Key键设置一个值,为避免死锁,并给定一个过期时间
    • 解锁:将Key键删除。但也不能乱删,不能说客户端1的请求将客户端2的锁给删除掉,只能自己删除自己的锁
    • 在这里插入图片描述
    • 超时:锁key要注意过期时间,不能长期占用
    • 面试中回答的主要考点
      • 加锁关键逻辑
      • 在这里插入图片描述
      • 解锁关键逻辑
      • 在这里插入图片描述
  • 单机模式中,一般都是用set/setnx+lua脚本搞定,想想它的缺点是什么?

多机案例

基于setnx的分布式锁有什么缺点?

在这里插入图片描述

  • 线程 1 首先获取锁成功,将键值对写入 redis 的 master 节点;在 redis 将该键值对同步到 slave 节点之前,master 发生了故障;redis 触发故障转移,其中一个 slave 升级为新的 master;此时新的 master 并不包含线程 1 写入的键值对,因此线程 2 尝试获取锁也可以成功拿到锁;此时相当于有两个线程获取到了锁,可能会导致各种预期之外的情况发生,例如最常见的脏数据。
  • 我们加的是排它独占锁,同一时间只能有一个建redis锁成功并持有锁,严禁出现2个以上的请求线程拿到锁。危险的

redis之父提出了Redlock算法解决这个问题

  • Redis也提供了Redlock算法,用来实现基于多个实例的分布式锁。锁变量由多个实例维护,即使有实例发生了故障,锁变量仍然是存在的,客户端还是可以完成锁操作。Redlock算法是实现高可靠分布式锁的一种有效解决方案,可以在实际开发中使用。

Redlock算法设计理念

设计理念
  • 该方案也是基于(set 加锁、Lua 脚本解锁)进行改良的,所以redis之父antirez 只描述了差异的地方,大致方案如下。
  • 假设我们有N个Redis主节点,例如 N = 5这些节点是完全独立的,我们不使用复制或任何其他隐式协调系统,为了取到锁客户端执行以下操作:
  • 在这里插入图片描述
  • 该方案为了解决数据不一致的问题,直接舍弃了异步复制只使用 master 节点,同时由于舍弃了 slave,为了保证可用性,引入了 N 个节点,官方建议是 5。阳哥本次教学演示用3台实例来做说明。
  • 客户端只有在满足下面的这两个条件时,才能认为是加锁成功。
    • 条件1:客户端从超过半数(大于等于N/2+1)的Redis实例上成功获取到了锁;
    • 条件2:客户端获取锁的总耗时没有超过锁的有效时间。
解决方案

在这里插入图片描述

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

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

相关文章

LeetCode刷题集(七)(2315.统计星号)

&#x1f626;学习目标&#xff1a;拿下LeetCode2315.统计星号题目 &#x1f624; 学完本章节知识即可掌握本题&#xff01; 学习内容&#xff1a;LeetCode2315.统计星号 &#x1f624;题目&#xff1a;给你一个字符串 s &#xff0c;每 两个 连续竖线 ‘|’ 为 一对 。换言之&…

知识图谱涉及技术点分析

文章目录 数据从哪里来为什么通常将知识图谱划分到NLP领域&#xff1f;常用NLP技术点分析只是NLP任务吗&#xff1f;graph embedding知识融合业务还是算法&#xff1f;知识图谱组成 数据从哪里来 是手动提取关系吗&#xff1f;数据很多&#xff0c;关系确难涉及大量NLP技术关系…

Ansible基础五——条件语句、循环语句、handlers、任务失败处理

文章目录 一、 循环语句1.1 单量循环1.2 多量循环1.3 老版本用法1.4 loopregister 二、条件判断2.1 根据变量状态判断2.2 根据变量是否存在判断2.3 根据事实判断2.4 多条件判断2.4.1 and用法2.4.2 or用法 2.5 循环判断2.6 根据上个任务结果判断 三、handlers处理程序四、任务失…

5月《中国数据库行业分析报告》正式发布,首发时序、实时数据库两大【全球产业图谱】

为了帮助大家及时了解中国数据库行业发展现状、梳理当前数据库市场环境和产品生态等情况&#xff0c;从2022年4月起&#xff0c;墨天轮社区行业分析研究团队出品将持续每月为大家推出最新《中国数据库行业分析报告》&#xff0c;持续传播数据技术知识、努力促进技术创新与行业生…

ubuntu20安装xrdp以及解决黑屏问题

1、安装xrdp sudo apt-get install xrdp 2、将xrdp用户加入到如下用户组 sudo adduser xrdp ssl-cert 3、重启xrdp sudo service xrdp restart 4、打开windows远程面&#xff0c;连接&#xff0c;如果出现黑屏 sudo -s sudo vim /etc/xrdp/startwm.sh 加入如下内容&#xff…

攻防世界-web-Web_php_unserialize

1. 题目描述&#xff1a;查看以下代码&#xff0c;获取flag 2. 思路分析 从代码中不难看出&#xff0c;这里共有三个地方需要绕过 2.1 __wakeup函数&#xff1a;若在对象的魔法函数中存在的__wakeup方法&#xff0c;那么之后再调用 unserilize() 方法进行反序列化之前则会先…

数据分析概述

数据分析概述 数据的性质数据的概念数据与信息的区别和联系 数据的类型按照度量尺度分按时间状况分 什么是数据分析数据分析的重要性数据分析的内容数据分析作用 数据分析的基本流程典型的数据分析的流程 数据分析方法对比分析法分组分析法定量数据分布分析——具体事例 结构分…

上海亚商投顾:沪指高开高走 地产股迎来久违反弹

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 三大指数今日高开高走&#xff0c;沪指午后涨近1%&#xff0c;深成指、创业板指涨超1.2%&#xff0c;上证50盘中大…

惠更斯定理和格林定理

惠更斯原理和格林定理 惠更斯原理显示了表面上的波场如何决定表面 S S S外的波场。惠更斯在17世纪启发性地表达了这一概念。但这个想法的数学表达是由于19世纪的乔治格林。这一概念可以在数学上表达为标量波和矢量波。矢量波情形的推导与标量波情形是同态的。但是标量波情况下…

少儿编程python-一级

少儿编程python 文章目录 前言CSP-J与CSP-S少儿编程证书含金量排名&#xff08;国家承认的少儿编程证书&#xff09;非专业级软件能力认证&#xff08;CSP-J/S&#xff09;青少年编程能力等级测试&#xff08;CPA&#xff09;蓝桥杯青少年信息技术等级考试全国青少年软件编程等…

造船厂事故/风险(背景+官方统计数据)

造船厂事故/风险&#xff08;背景官方统计数据&#xff09; 船厂工地常见事故船厂事故:发人深省的伤害统计船厂工地常见的风险有哪些? 造船业是周期性的、资本密集型的行业。更严格的环境法规于2020年初生效&#xff0c;引发了对抑制船舶废气硫排放技术的需求。与此同时&#…

数据标记工具

检测分割标定 labelstudio https://labelstud.io/sudo apt install libpq-dev python3-devconda activate paddle_envpip install label-studiolabel-studio startlabel-studio --data-dir /data/data_label_studio<View><Image name"image" value"$im…

【shiro】shiro整合JWT——2.如何整合

前言 shiro整合JWT系列&#xff0c;主要记录核心思路–如何在shiroredis整合JWTToken。 上一篇中&#xff0c;我们知道了需要创建JwtToken、JwtUtil、JwtFilter。 该篇主要讲如何在shiro框架中&#xff0c;配置Jwt。 ps&#xff1a;本文主要以记录核心思路为主。 1、ShiroCon…

如何零基础自学黑客?

我经常会看到这一类的问题&#xff1a; 学习XXX知识没效果&#xff1b;学习XXX技能没方向&#xff1b;学习XXX没办法入门&#xff1b; 给大家一个忠告&#xff0c;如果你完全没有基础的话&#xff0c;前期最好不要盲目去找资料学习&#xff0c;因为大部分人把资料收集好之后&…

Android中的WorkManager

Android中的WorkManager 在后台运行任务会消耗设备有限的资源&#xff0c;如RAM和电池。这可能会导致用户体验不佳。例如&#xff0c;后台任务可能会降低设备的电池寿命或用户在观看视频、玩游戏、使用相机等时可能会遇到设备性能不佳的情况。 为了提高电池性能&#xff0c;An…

关于人力资源管理职能,你需要知道的事

每个成功的企业都有一个称职的人力资源部门。它是任何企业的重要组成部分&#xff0c;是员工和管理层之间的纽带。人力资源涵盖影响组织人员的所有任务&#xff0c;从基本的人力资源活动到战略决策。 对于任何希望可持续发展的企业来说&#xff0c;人力资源管理职能的重要性不…

0802数量积向量积混合积-向量代数与空间解析几何

文章目录 1 两向量的数量积1.1 引例1.2 定义1.3 推论1.4 运算规律1.4 数量积的坐标表示 2 两向量的向量积2.1 定义2.2 重要结论2.3 几何意义&#xff08;向量积模&#xff09;2.4 向量积的运算规律2.5 向量积的坐标表示 3 向量的混合积3.1 混合积的定义3.2 混合积的坐标表示3.3…

初识报表引擎-FineReport

简介 提到报表引擎大家可能都会说帆软。目前商用的比较突出的两个报表引擎&#xff1a;分别是帆软FineReport、RDP报表引擎&#xff0c;其中帆软功能突出且非常完整但是价格较高&#xff0c;RDP功能相对完整但是不够强大貌似还有些BUG&#xff0c;不过价格很低。就目前的情况来…

《模板的进阶》

本文主要介绍C模板知识&#xff0c;包括模板的参数类型&#xff0c;模板的特化&#xff0c;模板的分离编译 文章目录 思维导图一、非类型模板参数二、模板的特化2.1模板特化的概念2.2函数模板特化2.3类模板的特化2.3.1全特化2.3.2偏特化 2.4非类型模板参数也是可以特化的 三、模…

Android 调用系统隐藏的类和方法

1.Android系统隐藏的类和方法 阅读Android源码时&#xff0c;会发现很多被UnsupportedAppUsage注解的方法&#xff0c;这些方法不能被外部应用访问。 比如Android中的PackageParser类&#xff0c;这个类是在android.content.pm包下面&#xff1a; 可以看到这个类是隐藏的&…