Redis的操作以及SpringCache框架

news2024/11/15 7:58:38

目录

一.什么是Redis?

二.Redis的相关知识:

三.如何操作Redis?

1,常用命令:

2.Spring Data Redis

(1) pom.xml 配置:

(2)配置Redis数据源:

(3)编写配置类,创建RedisTemplate对象:

(4)通过RedisTemplate对象操作Redis:

四.SpringCache框架:

1.什么是SpringCache框架?

2.SpringCache框架的相关注解:

 3.SpringCache框架的使用:

(1)pom.xml文件:

(2)在启动类上家@EnableCaching注解来开启缓存注解功能

(3)三个注解的使用:

2.@Cacheable:

3.@CacheEvict:


引言:本内容与B站黑马的苍穹外卖项目配套,总结一些Redis的知识点以及苍穹外卖项目的代码解析,希望该内容能给你带来帮助......

一.什么是Redis?

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

与MySQL数据库不同的是,Redis的数据是存在内存中的。它的读写速度非常快,每秒可以处理超过10万次读写操作。因此redis被广泛应用于缓存,另外,Redis也经常用来做分布式锁。除此之外,Redis支持事务、持久化、LUA 脚本、LRU 驱动事件、多种集群方案。

在同一个时间段内,向数据库进行大量操作(例如:添加大量的内容),就会频繁的查询数据库,导致数据库访问压力过大,最终导致系统响应慢、体验差,这个时候我们就需要使用Redis来缓解数据库的压力。

二.Redis的相关知识:

Redis是通过键值对的形式存储数据(key-value)。

key ->字符串

value -> String(字符串) , hash(哈希/散列) , list(有序列表,相当于Java的队列,可重复) , set(集合,无需不可重) , zset(集合,有序不可重)

想要进阶可以查看下面的博客:

Redis?它主要用来什么的-CSDN博客

下面是Redis的操作过程

优点:

  • 加快了响应速度
  • 减少了对数据库的读操作,数据库的压力降低

缺点:

  • 内存容量相对硬盘小

  • 缓存中的数据可能与数据库中数据不一致

  • 内存断电就会清空数据,造成数据丢失

三.如何操作Redis?

1,常用命令:

我们先下载Windows版的redis安装包进行安装,然后在cmd命令行中输入redis-server.exe redis.windows.conf,然后在打开新的cmd运行redis-cli.exe -h localhost -p 6379 -a 密码,当Redis服务启动成功后,可通过客户端进行连接。

我们也是有像操作MySQL一样的命令来操作Redis,这里给大家分享Redis图形化工具Another Redis Desktop Manager

这个常用命令就不过多叙述了...

2.Spring Data Redis

Spring Data Redis ->Redis的Java客户端,以至于我们可以在idea利用Java代码来操作Redis。

(1) pom.xml 配置:

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

(2)配置Redis数据源:

在application.yml文件中:

spring:
  redis:
    host: localhost   #ip
    port: 6379        #端口号
    password: ******  #Redis的密码
    database: 0       #指定使用哪个数据源(可不写,但默认是DB0)

(3)编写配置类,创建RedisTemplate对象:

我们使用RedisTemplate这个类对象内封装的方法来操作Redis,所以我们在配置类内创建RedisTemplate并用Bean注解将其交给Spring容器管理。

@Configuration
@Slf4j
public class RedisConfiguration {
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        log.info("开始创建Redis模板对象...0");
        RedisTemplate redisTemplate = new RedisTemplate();
        //设置redis的连接工厂对象
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //设置redis key的序列化器,这里反应的是Redis图形化工具上value值的不同
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

(4)通过RedisTemplate对象操作Redis:

String类型:redisTemplate.opsForValue()

hash类型:redisTemplate.opsForHash()

list类型:redisTemplate.opsForList()

set类型:redisTemplate.opsForSet()

zset类型:redisTemplate.opsForZSet()

其内部封装有相关操作Redis的方法,可以通过依赖注入的方式调用相关方法。

下面可以看一下测试类操作String类型来理解一下:

@SpringBootTest
public class SpringDataRedisTest {
    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    public void testString(){
        redisTemplate.opsForValue().set("city","北京");
        String city = (String)redisTemplate.opsForValue().get("city");
        System.out.println(city);
        redisTemplate.opsForValue().set("code","1234",3, TimeUnit.MINUTES);
        redisTemplate.opsForValue().setIfAbsent("lock","1");
        redisTemplate.opsForValue().setIfAbsent("lock","2");
    }
}

根据黑马苍穹外卖的业务代码(查询操作)来具体理解一下:

@RestController("userDishController")
@RequestMapping("/user/dish")
@Slf4j
@Api(tags = "C端-菜品浏览接口")
public class DishController {
  
    @GetMapping("/list")
    @ApiOperation("根据分类id查询菜品")
    public Result<List<DishVO>> list(Long categoryId) {

        //构造redis中的key,规则:dish_分类id
        String key = "dish_" + categoryId;
        //查询redis中是否存在菜品数据
        List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);
        //判断是否存在缓存
        if(list != null && list.size() > 0){
            return Result.success(list);
        }
        //如果不存在缓存
        Dish dish = new Dish();
        dish.setCategoryId(categoryId);
        dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品

        list = dishService.listWithFlavor(dish);
        //将查询到的数据放入redis缓存中
        redisTemplate.opsForValue().set(key,list);

        return Result.success(list);
    }
}

四.SpringCache框架:

1.什么是SpringCache框架?

基于注解的使用来操作Redis的框架。

2.SpringCache框架的相关注解:

  • @Cacheable:在方法执行前查看是否有缓存对应的数据,如果有直接返回数据,如果没有调用方法获取数据返回,并缓存起来。
  • @CacheEvict:将一条或多条数据从缓存中删除。
  • @CachePut:将方法的返回值放到缓存中
  • @EnableCaching:开启缓存注解功能

 3.SpringCache框架的使用:

(1)pom.xml文件:

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

(2)在启动类上家@EnableCaching注解来开启缓存注解功能

@SpringBootApplication
@EnableCaching //开发缓存注解功能
@Slf4j
public class SkyApplication {
    public static void main(String[] args) {
        SpringApplication.run(SkyApplication.class, args);
        log.info("server started");
    }
}

(3)三个注解的使用:

我们在介绍这三个注解前需要知道使用注解后存入Redis的key值是什么?

这个时候就需要介绍 cacheNames,key,allEntries:

用@CachePut举例子(共五种表示方法):

①.

@CachePut(cacheNames = "名字" , key = "#形参.形参内属性") 
//key的生成为:名字::属性值

 eg:

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private UserMapper userMapper;

    @PostMapping
    @CachePut(cacheNames = "userCache" , key = "#user.id") //key的生成为:userCache::2
    public User save(@RequestBody User user){
        userMapper.insert(user);
        return user;
    }
}

②p0代表第一个形参

@CachePut(cacheNames = "名字" , key = "#p0.属性") 
//key的生成为:名字::属性值

③a0代表第一个形参

@CachePut(cacheNames = "名字" , key = "#a0.属性") 
//key的生成为:名字::属性值

④root.args[0]代表第一个形参

@CachePut(cacheNames = "名字" , key = "#root.args[0].属性") 
//key的生成为:名字::属性值

⑤result是代表返回值

@CachePut(cacheNames = "名字" , key = "#result.属性") 
//key的生成为:名字::属性值

随后还要注意@Cacheable不能使用result

1.@CachePut:

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private UserMapper userMapper;

    @PostMapping
    @CachePut(cacheNames = "userCache" , key = "#user.id") //key的生成为:userCache::2
    public User save(@RequestBody User user){
        userMapper.insert(user);
        return user;
    }
}
2.@Cacheable:
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private UserMapper userMapper;

    @GetMapping
    @Cacheable(cacheNames = "userCache" , key = "#id")
    public User getById(Long id){
        User user = userMapper.getById(id);
        return user;
    }
}
3.@CacheEvict:

删除一条数据时:

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private UserMapper userMapper;

    @DeleteMapping
    @CacheEvict(cacheNames = "userCache" ,  key = "#id")
    public void deleteById(Long id){
        userMapper.deleteById(id);
    }
}

删除多条数据时:allEntries翻译为所有键值对

@RestController
@RequestMapping("/admin")
@Slf4j
public class UserController {

    @Autowired
    private UserMapper userMapper;

    @PutMapping
    @ApiOperation("修改套餐")
    @CacheEvict(cacheNames = "setmealCache" , allEntries = true)
    public Result update(@RequestBody SetmealDTO setmealDTO){
        setmealService.update(setmealDTO);
        return Result.success();
    }
}

 五.实战应用:

1.设置店铺状态:

如果我们想设置一个餐厅是营业状态还是打烊,我们可以设置Integer status属性封装状态,并在Redis中创建key-value,之后获取可以在Redis获取。

@RestController("adminShopController")
@RequestMapping("/admin/shop")
@Api(tags = "店铺相关接口")
@Slf4j
public class ShopController {
    public static final String KEY = "SHOP_STATUS";

    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 设置店铺的营业状态
     * @param status
     * @return
     */
    @PutMapping("/{status}")
    @ApiOperation("设置店铺的营业状态")
    public Result setStatus(@PathVariable Integer status){
        log.info("设置店铺的营业状态为:{}",status == 1 ? "营业中" : "打烊中");
        redisTemplate.opsForValue().set(KEY,status);
        return Result.success();
    }

    /**
     * 获取店铺的营业状态
     * @return
     */
    @GetMapping("/status")
    @ApiOperation("获取店铺的营业状态")
    public Result<Integer> getStatus(){
        Integer status = (Integer)redisTemplate.opsForValue().get(KEY);
        log.info("获取店铺的营业状态:{}",status == 1 ? "营业中" : "打烊中");
        return Result.success(status);
    }
}

 2.用户查询菜品的Redis缓存操作:

将查询过的结果缓存,以便于下次直接在Redis中获得缓存来减少数据库的压力 

(1)将用户最开始首次进入页面执行的接口设置@Cacheable,能够直接将回调的数据在Redis缓存,以便下一次直接通过注解的作用直接返回数据,而无需调用接口内方法:

@RestController("userSetmealController")
@RequestMapping("/user/setmeal")
@Api(tags = "C端-套餐浏览接口")
public class SetmealController {
    @Autowired
    private SetmealService setmealService;

    /**
     * 条件查询
     *
     * @param categoryId
     * @return
     */
    @GetMapping("/list")
    @ApiOperation("根据分类id查询套餐")
    @Cacheable(cacheNames = "setmealCache" , key = "#categoryId")  //key为 setmealCache::100
    public Result<List<Setmeal>> list(Long categoryId) {
        Setmeal setmeal = new Setmeal();
        setmeal.setCategoryId(categoryId);
        setmeal.setStatus(StatusConstant.ENABLE);

        List<Setmeal> list = setmealService.list(setmeal);
        return Result.success(list);    
    }
}

 (2)在商家想要进行增删改的操作,以及与菜品可能会出现缓存问题的接口进行清空缓存,以免缓存与更改后的数据不一致造成错误:(方法删除)

下面的代码为对菜品增删改,以及菜品起售停售进行清空缓存,为了节省以及美观,这里封装了cleanCache()方法,随后仅需要传递形参也就是想要删除的缓存的key格式,然后调用方法redisTemplate.keys(key)获得所有该格式下的key并用set集合封装,随后调用delete方法删除所有key就可以达到清空缓存的目的

@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {

    @Autowired
    private DishService dishService;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 封装的清理缓存数据方法
     * @param patten
     */
    private void cleanCache(String patten){
        //清理删除的菜品缓存
        Set keys= redisTemplate.keys(patten);//将所有以dish_的格式的key获取出来
        redisTemplate.delete(keys);
    }

    /**
     * 新增菜品
     * @param dishDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增菜品")
    public Result save(@RequestBody DishDTO dishDTO){
        log.info("新增菜品:{}",dishDTO);
        dishService.saveWithFlavor(dishDTO);

        //清理缓存
        String key = "dish_" + dishDTO.getCategoryId();
        cleanCache(key);
        return Result.success();
    }

    /**
     * 菜品的批量删除
     * @param ids
     * @return
     */
    @DeleteMapping
    @ApiOperation("菜品的批量删除")
    public Result delete(@RequestParam List<Long> ids){
        log.info("菜品的批量删除:{}",ids);
        dishService.deleteBatch(ids);

        //清理删除的菜品缓存
        cleanCache("dish_*");
        return Result.success();
    }

    /**
     * 修改菜品
     * @param dishDTO
     * @return
     */
    @PutMapping
    @ApiOperation("修改菜品")
    public Result update(@RequestBody DishDTO dishDTO){
        log.info("修改菜品,{}",dishDTO);
        dishService.updateWithFlavor(dishDTO);

        //清理缓存数据
        //清理删除的菜品缓存
        cleanCache("dish_*");

        return Result.success();
    }

    /**
     * 菜品起售停售
     * @param status
     * @param id
     * @return
     */
    @PostMapping("/status/{status}")
    @ApiOperation("菜品起售停售")
    public Result<String> startOrStop(@PathVariable Integer status, Long id){
        dishService.startOrStop(status,id);

        //清理删除的菜品缓存
        cleanCache("dish_*");

        return Result.success();
    }
}

(3)在商家想要进行增删改的操作,以及与套餐可能会出现缓存问题的接口进行清空缓存,以免缓存与更改后的数据不一致造成错误:(注解删除)

跟上面的一样,只不过这次使用我们SpringCache框架的注解来进行清空缓存的操作。

@RestController
@RequestMapping("/admin/setmeal")
@Api(tags = "套餐相关接口")
@Slf4j
public class SetmealController {
    @Autowired
    private SetmealService setmealService;

    /**
     * 新增套餐
     * @param setmealDTO
     * @return
     */
    @PostMapping
    @ApiOperation("新增套餐")
    @CacheEvict(cacheNames = "setmealCache" , key = "#setmealDTO.categoryId") //key为 setmealCache::100
    public Result save(@RequestBody SetmealDTO setmealDTO){
        setmealService.saveWithDish(setmealDTO);
        return Result.success();
    }

    /**
     * 批量删除套餐
     * @return
     */
    @DeleteMapping
    @ApiOperation("批量删除套餐")
    @CacheEvict(cacheNames = "setmealCache" , allEntries = true)
    public Result delete(@RequestParam List<Long> ids){
        setmealService.deleteBatch(ids);
        return Result.success();
    }

    /**
     * 修改套餐
     * @param setmealDTO
     * @return
     */
    @PutMapping
    @ApiOperation("修改套餐")
    @CacheEvict(cacheNames = "setmealCache" , allEntries = true)
    public Result update(@RequestBody SetmealDTO setmealDTO){
        setmealService.update(setmealDTO);
        return Result.success();
    }

    /**
     * 套餐起售停售
     * @param status
     * @param id
     * @return
     */
    @PostMapping("/status/{status}")
    @ApiOperation("套餐起售停售")
    @CacheEvict(cacheNames = "setmealCache" , allEntries = true)
    public Result startOrStop(@PathVariable Integer status, Long id) {
        setmealService.startOrStop(status, id);
        return Result.success();
    }
}

好了,Redis的操作大致内容就这些,读完可以了解缓存击穿、缓存穿透、缓存雪崩这三个问题,今天内容就到这里,感谢收看!!!

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

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

相关文章

IE11添加收藏、关闭窗口时弹出的对话框字体又大又粗很难看的解决办法

原因已查明&#xff0c;在win7 sp1 32位系统下&#xff0c;安装“2020-01 适用于基于 x86 的系统的 Windows 7 月度安全质量汇总&#xff08;KB4534310&#xff09;”这个更新会导致IE11的窗口字体变大变粗&#xff0c;把这个更新卸载了就可以了&#xff0c;无需重装IE11浏览器…

【芯智雲城】详解智能电机驱动在汽车中的应用

随着汽车系统中传统的机械化设计被电子化设计逐渐取代&#xff0c;电机在汽车电子化系统中扮演的角色越来越重要。例如在汽车的动力系统中&#xff0c;由传统的燃油发动机逐步发展为现在的有刷同步电机、感应电机&#xff0c;真正的实现了新能源车的动力革命&#xff1b;传统的…

论文快过(图像配准|Coarse_LoFTR_TRT)|适用于移动端的LoFTR算法的改进分析 1060显卡上45fps

项目地址&#xff1a;https://github.com/Kolkir/Coarse_LoFTR_TRT 创建时间&#xff1a;2022年 相关训练数据&#xff1a;BlendedMVS LoFTR [19]是一种有效的深度学习方法&#xff0c;可以在图像对上寻找合适的局部特征匹配。本文报道了该方法在低计算性能和有限内存条件下的…

Android AutoSize屏幕适配:适配不同屏幕大小的尺寸,让我们无需去建立多个尺寸资源文件

目录 AutoSize是什么 AutoSize如何使用 一、AndroidautoSize是什么 在开发产品的时候&#xff0c;我们会遇到各种各样尺寸的屏幕&#xff0c;如果只使用一种尺寸去定义控件、文字的大小&#xff0c;那么到时候改起来就头皮发麻。以前使用dime的各种类库&#xff0c;文件太多…

一种提供改进的通道迁移率和高可靠性的SiC沟槽MOSFET概念

来源&#xff1a;A SiC Trench MOSFET concept offering improved channel mobility and high reliability&#xff08;2017 19th European Conference on Power Electronics and Applications (EPE’17 ECCE Europe)&#xff09; 摘要 这项工作讨论了与硅基同类产品相比&…

性能测试工具 - Siege

在快速发展的技术时代&#xff0c;网站和应用的性能对于用户体验和业务成功至关重要。作为测试工程师&#xff0c;找到高效的性能测试工具显得尤为重要。今天&#xff0c;我们来聊聊一个备受推崇的性能测试工具——Siege。 为什么Siege能够在众多性能测试工具中脱颖而出&#x…

C++ 内存与编译问题总结

目录 C内存结构 作用域与生存周期 堆与栈 内存对齐 智能指针 shared_ptr 循环引用问题 编译与链接 内存泄漏 补充问题 include “ ”与<> 大端与小端 C内存结构 C程序内存分区 代码区 文件中所有的函数代码、常量以及字符串常量只读&#xff0c;保护程序不会被…

在invidia jetpack4.5.1上运行c++版yolov8(tensorRT)

心路历程(可略过) 为了能在arm64上跑通yolov8,我试过很多很多代码,太多对库版本的要求太高了; 比如说有一个是需要依赖onnx库的,(https://github.com/UNeedCryDear/yolov8-opencv-onnxruntime-cpp) 运行成功了报错error: IOrtSessionOptionsAppendExecutionProvider C…

力扣高频SQL 50题(基础版)第十八题

文章目录 力扣高频SQL 50题&#xff08;基础版&#xff09;第十八题1633. 各赛事的用户注册率题目说明思路分析实现过程准备数据实现方式结果截图 力扣高频SQL 50题&#xff08;基础版&#xff09;第十八题 1633. 各赛事的用户注册率 题目说明 用户表&#xff1a; Users --…

嵌入式Python、ROS、SLAM、WebSocket和Node.js:智能巡逻监控安防机器人设计流程(代码示例)

项目概述 随着智能技术的发展&#xff0c;智能巡逻机器人在安防、监控和巡逻等领域的应用越来越广泛。本文将介绍一个结合嵌入式系统、机器人技术和后端开发的智能巡逻机器人。该机器人能够自主导航&#xff0c;实时检测异常情况&#xff08;如火灾或入侵者&#xff09;&#…

免费【2024】springboot 超市在线销售系统的设计与实现

博主介绍&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化…

Adobe Photoshop(Ps)安装包软件下载

一、Adobe Photoshop简介 Adobe Photoshop&#xff08;简称PS&#xff09;是由Adobe Systems公司开发的图像处理软件&#xff0c;它是一款集图像扫描、编辑修改、图像制作、广告创意、图像输入与输出于一体的图形图像处理软件。广泛应用于专业测评、平面设计、广告摄影、影像创…

通过限制访问,实现纯私有Docker镜像

怎么会不过审呢?没有敏感信息呀。 For obvious reasons,Many Docker image repositories are inaccessible,The official warehouse has also been filtered by the firewall,So write about how to build a self use Docker image using CloudFlares Workers and Pages. …

SQL Server 设置端口号:详细步骤与注意事项

目录 一、了解SQL Server端口号的基础知识 1.1 默认端口号 1.2 静态端口与动态端口 二、使用SQL Server配置管理器设置端口号 2.1 打开SQL Server配置管理器 2.2 定位到SQL Server网络配置 2.3 修改TCP/IP属性 2.4 重启SQL Server服务 三、注意事项 3.1 防火墙设置 3…

VSCode 解决 pylint 报错 No name QWidget in module PyQt5.QtWidgets

问题 启用了 VSCode 的 Pylint 插件, 即便 Python 环境中安装了 PyQt5, 也无法正确解析 PyQt5 的导入 PyQt5 底层代码是用 C/C 写的, pylint 默认不会深入解析 pylint doesn’t load any C extensions by default, because those can run arbitrary code. 解决 修改 Settings…

Internet Download Manager(IDM)2024中文版本有哪些新功能?6.42版本功能介绍

1. Internet Download Manager&#xff08;IDM&#xff09;是一款功能强大的下载管理器&#xff0c;支持所有流行的浏览器&#xff0c;并可提升下载速度高达5倍。 2. IDM具有智能下载逻辑加速器&#xff0c;可以设置文件下载优先级、分块下载等&#xff0c;提高下载效率。 IDM…

网站用HTTP访问的危害以及如何升级HTTPS访问

在互联网世界中&#xff0c;数据传输的安全性是至关重要的。我们每天都在网络上进行各种操作&#xff0c;从浏览网页、购物到银行转账&#xff0c;每一项活动都涉及敏感信息的传递。然而&#xff0c;在这个过程中&#xff0c;我们的数据可能面临被窃取、篡改或滥用的风险。这正…

Vue的安装配置

1.安装node js Node.js — 在任何地方运行 JavaScript (nodejs.org) 2.测试nodejs是否安装成功 node -v npm -v3.通过npm 安装 vue npm install -g vue/cli4.测试vue是否安装成功 vue --version5.打开PyCharm&#xff0c;创建项目&#xff1a;flask-web vue create flask…

深入理解Python装饰器:从基础到进阶

引言 在Python中&#xff0c;装饰器是一种强大的工具&#xff0c;它允许程序员以一种简洁而优雅的方式修改函数或类的行为。装饰器本质上是一个接受函数作为参数的函数&#xff0c;并返回一个新的函数。本文将从装饰器的基础开始介绍&#xff0c;并逐步深入到一些高级用法。 …

鸿蒙应用框架开发【多线程任务】

多线程任务 介绍 本示例通过ohos.taskpool和ohos.worker接口&#xff0c;展示了如何启动worker线程和taskpool线程。 效果预览 使用说明 在主界面&#xff0c;可以点击字符串排序和拷贝文件按钮进入对应的界面&#xff1b; 点击字符串排序按钮进入多线程界面&#xff1a; w…