个人博客项目笔记_05

1. ThreadLocal内存泄漏

在这里插入图片描述

ThreadLocal 内存泄漏是指由于没有及时清理 ThreadLocal 实例所存储的数据,导致这些数据在线程池或长时间运行的应用中累积过多,最终导致内存占用过高的情况。

内存泄漏通常发生在以下情况下:

  1. 线程池场景下的 ThreadLocal 使用不当: 在使用线程池时,如果线程被重用而没有正确清理 ThreadLocal 中的数据,那么下次使用这个线程时,它可能会携带上一次执行任务所遗留的数据,从而导致数据累积并消耗内存。
  2. 长时间运行的应用中未清理 ThreadLocal 数据: 在一些长时间运行的应用中,比如 Web 应用,可能会创建很多 ThreadLocal 实例并存储大量数据。如果这些数据在使用完后没有及时清理,就会导致内存泄漏问题。
  3. 没有使用 remove() 方法清理 ThreadLocal 数据: 在使用完 ThreadLocal 存储的数据后,如果没有调用 remove() 方法清理数据,就会导致数据长时间存在于 ThreadLocal 中,从而可能引发内存泄漏。

实线代表强引用,虚线代表弱引用

每一个Thread维护一个ThreadLocalMap, key为使用弱引用的ThreadLocal实例,value为线程变量的副本。

强引用,使用最普遍的引用,一个对象具有强引用,不会被垃圾回收器回收。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不回收这种对象。

如果想取消强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样可以使JVM在合适的时间就会回收该对象。

弱引用,JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。

2. 文章详情

2.1 接口说明

接口url:/articles/view/{id}

请求方式:POST

请求参数:

参数名称参数类型说明
idlong文章id(路径参数)

返回数据:

{"success": true,"code": 200,"msg": "success","data": "token"
}

2.2 涉及到的表

CREATE TABLE `blog`.`ms_article_body`  (`id` bigint(0) NOT NULL AUTO_INCREMENT,`content` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,`content_html` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,`article_id` bigint(0) NOT NULL,PRIMARY KEY (`id`) USING BTREE,INDEX `article_id`(`article_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 38 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
package com.cherriesovo.blog.dao.pojo;import lombok.Data;@Data
public class ArticleBody {	//文章详情表private Long id;private String content;private String contentHtml;private Long articleId;
}
#文章分类
CREATE TABLE `blog`.`ms_category`  (`id` bigint(0) NOT NULL AUTO_INCREMENT,`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,`category_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
package com.cherriesovo.blog.dao.pojo;import lombok.Data;@Data
public class Category {private Long id;private String avatar;private String categoryName;private String description;
}

2.3 Controller

//json数据进行交互
@RestController
@RequestMapping("articles")
public class ArticleController {/** 通过id获取文章* */@PostMapping("view/{id}")//@PathVariable("id") 注解用于将 URL 中的 {id} 赋值给 articleId 参数。public Result findArticleById(@PathVariable("id") Long articleId) {	return articleService.findArticleById(articleId);}
}

2.4 Service

 ArticleVo findArticleById(Long id);
public interface ArticleService {//查看文章详情Result findArticleById(Long articleId);
}
package com.cherriesovo.blog.vo;import lombok.Data;import java.util.List;@Data
public class ArticleVo {private Long id;private String title;private String summary;private int commentCounts;private int viewCounts;private int weight;/*** 创建时间*/private String createDate;private String author;private ArticleBodyVo body;private List<TagVo> tags;private CategoryVo category;}
@Service
public class ArticleServiceImpl implements ArticleService {@Autowiredprivate ArticleMapper articleMapper;@Autowiredprivate TagService tagService;@Autowiredprivate SysUserService sysUserService;@Autowiredprivate CategoryService categoryService;public ArticleVo copy(Article article,boolean isAuthor,boolean isBody,boolean isTags,boolean isCategory){ArticleVo articleVo = new ArticleVo();BeanUtils.copyProperties(article, articleVo);articleVo.setCreateDate(new DateTime(article.getCreateDate()).toString("yyyy-MM-dd HH:mm"));//并不是所有的接口都需要标签,作者信息if(isTags){Long articleId = article.getId();articleVo.setTags(tagService.findTagsByArticleId(articleId));}if(isAuthor){Long authorId = article.getAuthorId();//getNickname()用于获取某个对象或实体的昵称或别名articleVo.setAuthor(sysUserService.findUserById(authorId).getNickname());}if (isBody){Long bodyId = article.getBodyId();articleVo.setBody(findArticleBodyById(bodyId));}if (isCategory){Long categoryId = article.getCategoryId();articleVo.setCategory(categoryService.findCategoryById(categoryId));}return articleVo;}private List<ArticleVo> copyList(List<Article> records,boolean isAuthor,boolean isBody,boolean isTags) {List<ArticleVo> articleVoList = new ArrayList<>();for (Article article : records) {ArticleVo articleVo = copy(article,isAuthor,false,isTags,false);articleVoList.add(articleVo);}return articleVoList;}private List<ArticleVo> copyList(List<Article> records,boolean isAuthor,boolean isBody,boolean isTags,boolean isCategory) {List<ArticleVo> articleVoList = new ArrayList<>();for (Article article : records) {ArticleVo articleVo = copy(article,isAuthor,isBody,isTags,isCategory);articleVoList.add(articleVo);}return articleVoList;}@Autowiredprivate ArticleBodyMapper articleBodyMapper;private ArticleBodyVo findArticleBodyById(Long bodyId) {ArticleBody articleBody = articleBodyMapper.selectById(bodyId);ArticleBodyVo articleBodyVo = new ArticleBodyVo();articleBodyVo.setContent(articleBody.getContent());//setContent()是articleBodyVo的set方法return articleBodyVo;}@Overridepublic List<ArticleVo> listArticlesPage(PageParams pageParams) {//  分页查询article数据库表QueryWrapper<Article> queryWrapper = new QueryWrapper<>();Page<Article> page = new Page<>(pageParams.getPage(),pageParams.getPageSize());Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);List<ArticleVo> articleVoList = copyList(articlePage.getRecords(),true,false,true);return articleVoList;}@Overridepublic Result hotArticle(int limit) {LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.orderByDesc(Article::getViewCounts);   //根据浏览量倒序queryWrapper.select(Article::getId,Article::getTitle);queryWrapper.last("limit " + limit);//select id,title from article order by view_counts desc limit 5List<Article> articles = articleMapper.selectList(queryWrapper);return Result.success(copyList(articles,false,false,false));}@Overridepublic Result newArticles(int limit) {LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.orderByDesc(Article::getCreateDate);queryWrapper.select(Article::getId,Article::getTitle);queryWrapper.last("limit "+limit);//select id,title from article order by create_date desc limit 5List<Article> articles = articleMapper.selectList(queryWrapper);return Result.success(copyList(articles,false,false,false));}@Overridepublic Result listArchives() {List<Archives> archivesList = articleMapper.listArchives();return Result.success(archivesList);}@Overridepublic Result findArticleById(Long articleId) {/** 1、根据id查询文章信息* 2、根据bodyId和categoryId去做关联查询* */Article article = this.articleMapper.selectById(articleId);ArticleVo articleVo = copy(article, true, true, true,true);return Result.success(articleVo);}
}
package com.cherriesovo.blog.vo;import lombok.Data;@Data
public class CategoryVo {private Long id;private String avatar;private String categoryName;
}
package com.cherriesovo.blog.vo;import lombok.Data;@Data
public class ArticleBodyVo {private String content;
}
package com.cherriesovo.blog.service;import com.cherriesovo.blog.vo.CategoryVo;import java.util.List;public interface CategoryService {CategoryVo findCategoryById(Long categoryId);
}
@Service
public class CategoryServiceImpl implements CategoryService {@Autowiredprivate CategoryMapper categoryMapper;@Overridepublic CategoryVo findCategoryById(Long categoryId) {Category category = categoryMapper.selectById(categoryId);CategoryVo categoryVo = new CategoryVo();BeanUtils.copyProperties(category,categoryVo);return categoryVo;}
}
package com.cherriesovo.blog.dao.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cherriesovo.blog.dao.pojo.ArticleBody;public interface ArticleBodyMapper extends BaseMapper<ArticleBody> {
}
package com.cherriesovo.blog.dao.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cherriesovo.blog.dao.pojo.Category;public interface CategoryMapper extends BaseMapper<Category> {
}

2.5 测试

3. 使用线程池 更新阅读次数

3.1 线程池配置

  1. taskExecutor 是一个线程池对象,在这段代码中通过 @Bean("taskExecutor") 注解定义并配置了一个线程池,并将其命名为 “taskExecutor”。
  2. asyncServiceExecutor() 方法是一个 Bean 方法,用于创建并配置一个线程池,并以 taskExecutor 作为 Bean 的名称。
  3. ThreadPoolTaskExecutor 是 Spring 框架提供的一个实现了 Executor 接口的线程池
  4. 在方法中创建了一个 ThreadPoolTaskExecutor 实例 executor,并对其进行了一系列配置:
    • setCorePoolSize(5): 设置核心线程数为 5,即线程池在空闲时会保持 5 个核心线程。
    • setMaxPoolSize(20): 设置最大线程数为 20,即线程池中允许的最大线程数量。
    • setQueueCapacity(Integer.MAX_VALUE): 配置队列大小为整数的最大值,即任务队列的最大容量。
    • setKeepAliveSeconds(60): 设置线程活跃时间为 60 秒,即线程在空闲超过该时间后会被销毁。
    • setThreadNamePrefix("CherriesOvO博客项目"): 设置线程名称的前缀为 “CherriesOvO博客项目”。
    • setWaitForTasksToCompleteOnShutdown(true): 设置在关闭线程池时等待所有任务结束。
    • initialize(): 执行线程池的初始化。
  5. 最后,将配置好的线程池返回为一个 Executor Bean,供其他组件使用。
package com.cherriesovo.blog.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;@Configuration
@EnableAsync    //开启多线程
public class ThreadPoolConfig {@Bean("taskExecutor")public Executor asyncServiceExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 设置核心线程数executor.setCorePoolSize(5);// 设置最大线程数executor.setMaxPoolSize(20);//配置队列大小executor.setQueueCapacity(Integer.MAX_VALUE);// 设置线程活跃时间(秒)executor.setKeepAliveSeconds(60);// 设置默认线程名称executor.setThreadNamePrefix("CherriesOvO博客项目");// 设置等待所有任务结束后再关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true);//执行初始化executor.initialize();return executor;}
}

3.1 使用

  1. 通过 @Async("taskExecutor") 注解,该方法标记为异步执行,并指定了使用名为 “taskExecutor” 的线程池。

  2. articleMapper.update(articleUpdate, updateWrapper) 是一个 MyBatis-Plus 中的更新操作,用于更新数据库中的文章记录。

    update 方法接受两个参数:

    1. articleUpdate:表示需要更新的文章对象,其中包含了新的阅读量。
    2. updateWrapper:表示更新条件,即确定哪些文章需要被更新的条件。
  3. 这段代码通过 Thread.sleep(5000) 方法在当前线程中休眠了5秒钟。这样做的目的是为了模拟一个耗时操作,以展示在异步线程中执行的任务不会影响到主线程的执行。

package com.cherriesovo.blog.service;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cherriesovo.blog.dao.mapper.ArticleMapper;
import com.cherriesovo.blog.dao.pojo.Article;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;@Component
public class ThreadService {//期望此操作在线程池中执行,不会影响原有的主线程@Async("taskExecutor")public void updateArticleViewCount(ArticleMapper articleMapper, Article article){int viewCounts = article.getViewCounts();Article articleUpdate = new Article();articleUpdate.setViewCounts(viewCounts + 1);LambdaQueryWrapper<Article> updateWrapper = new LambdaQueryWrapper<>();updateWrapper.eq(Article::getId,article.getId());//设置一个 为了在多线程环境下 线程安全updateWrapper.eq(Article::getViewCounts,article.getViewCounts());//update article set view_count=? where view_count=? and id=?articleMapper.update(articleUpdate,updateWrapper);try {//睡眠5秒 证明不会影响主线程的使用,5秒后数据才会出现Thread.sleep(5000);
//            System.out.println("更新完成了");} catch (InterruptedException e) {e.printStackTrace();}}
}
@Service
public class ArticleServiceImpl implements ArticleService {@Autowiredprivate ThreadService threadService;@Overridepublic Result findArticleById(Long articleId) {/** 1、根据id查询文章信息* 2、根据bodyId和categoryId去做关联查询* */Article article = this.articleMapper.selectById(articleId);ArticleVo articleVo = copy(article, true, true, true,true);//查看完文章了,新增阅读数,有没有问题?//查看完文章之后,本应该直接返回数据了,这时候做了一个更新操作,更新时加写锁,阻塞其他读操作,性能比较低//更新增加此时接口的耗时,如果一旦更新出问题,不能影响查看文章的操作//线程池  可以把更新操作扔到线程池中去执行,和主线程就不相关了threadService.updateArticleViewCount(articleMapper,article);return Result.success(articleVo);}
}

3.3 测试

  • 2、根据bodyId和categoryId去做关联查询
    * */
    Article article = this.articleMapper.selectById(articleId);
    ArticleVo articleVo = copy(article, true, true, true,true);
    //查看完文章了,新增阅读数,有没有问题?
    //查看完文章之后,本应该直接返回数据了,这时候做了一个更新操作,更新时加写锁,阻塞其他读操作,性能比较低
    //更新增加此时接口的耗时,如果一旦更新出问题,不能影响查看文章的操作
    //线程池 可以把更新操作扔到线程池中去执行,和主线程就不相关了
    threadService.updateArticleViewCount(articleMapper,article);
    return Result.success(articleVo);
    }
    }

## 3.3 测试睡眠 ThredService中的方法 5秒,不会影响主线程的使用,即文章详情会很快的显示出来,不受影响

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

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

相关文章

牛客 NC252 多数组中位数【中等 模拟 Java,Go】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/b6bb0bce88894108bfc23e9b7b012420 思路 模拟&#xff0c;2数组合并一个数组helphelp长度为奇数&#xff0c;直接取中间值&#xff0c;为偶数&#xff0c;中间2个值&#xff0c;哪个小返回哪个参考答案Java imp…

结合ArcGIS+SWAT模型+Century模型:流域生态系统水-碳-氮耦合过程模拟

原文链接&#xff1a;结合ArcGISSWAT模型Century模型&#xff1a;流域生态系统水-碳-氮耦合过程模拟https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&tempkeyMTI2NV9sMGRZNUJoVkNVc1ZzSzRuMl9XXzhqX0R3cXpESWFwM1E4cFY4ejNqWFh3VUl0dlZkNWk4b20ydFdFTy1xS2ZObGN0Z0ZXSjly…

【学习路径】AI入门路线分享

近期整理飞书文档&#xff0c;一些权限被关掉了。看好多人在申请访问这个飞书文档&#xff0c;于是把它单独拿出来放在CSDN上&#xff0c;供大家参考~ 原视频地址&#xff1a;AI&#xff1a;从小白到入门&#xff0c;超详细人工智能成长路径分享_哔哩哔哩_bilibili 文章目录 1.…

OpenStack (T)部署trove

环境&#xff1a;Openstack&#xff08;T&#xff09; CentOS Linux release 7.9.2009 (Core) 正文&#xff1a; 1.控制节点安装trove软件包 # yum install openstack-trove-guestagent openstack-trove python-troveclient openstack-trove-ui –y2.创建数据库&#xff0c…

C++11 设计模式3. 工厂方法模式

简单工厂模式的遗留问题 //从上面的代码可以看到&#xff0c;简单工厂模式确实实现了new 出来具体对象&#xff0c; 和 业务逻辑的分离&#xff0c; //但是不符合 "开闭原则" //"开闭原则"说的是代码扩展性问题——对扩展开放&#xff0c;对修改关…

如何在OceanBase v4.2 中快速生成随机数据

在使用传统数据库如 MySQL 和 Oracle 时&#xff0c;由于缺乏多样化的随机数据生成方案&#xff0c;或者实现成本过高&#xff0c;构造随机数据的开发成本受到了影响。OceanBase在老版本中虽然有相应的解决方案&#xff0c;但语法复杂和性能较差等问题仍然存在。 现在&#xf…

【漏洞复现】润乾报表平台 InputServlet接口处存在任意文件上传漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

车内AR互动娱乐解决方案,打造沉浸式智能座舱体验

美摄科技凭借其卓越的创新能力&#xff0c;为企业带来了革命性的车内AR互动娱乐解决方案。该方案凭借自研的AI检测和渲染引擎&#xff0c;打造出逼真的数字形象&#xff0c;不仅丰富了车机娱乐内容&#xff0c;更提升了乘客与车辆的互动体验&#xff0c;让每一次出行都成为一场…

C语言 循环控制——while和do-while语句

目录 whiel语句 do-while语句 while与do-while的区别 计数控制的循环 ​编辑标记控制的循环 whiel语句 do-while语句 while与do-while的区别 计数控制的循环 标记控制的循环

怎么构建临床知识图谱?

怎么构建临床知识图谱? 构建临床知识图谱传统临床指南的局限性决策的不确定性和个体差异构建真实临床知识图谱构建真实世界的临床事件图谱基于书本抽取的知识来自哪些书本&#xff1f;如何保证这些知识是最新的知识&#xff1f;如何不断融入最新的医学研究进展&#xff0c;从而…

挖掘未来:私有LTE/5G网络驱动智慧矿山的自动化

私有LTE/5G网络为世界上一些最偏远的角落提供无线连接。如果没有无线通信网络&#xff0c;各行业就无法满足增加产量、降低运营成本和减少环境破坏的需求。 在本案例研究中&#xff0c;我们着眼于自动化如何改变无线网络的动态。智慧矿山要求运营商无缝集成多个系统和应用程序…

探索Web3的奇迹:数字时代的新前景

在数字化时代的潮流中&#xff0c;我们不可避免地迎来了一个全新的篇章——Web3时代的到来。在这个时代中&#xff0c;区块链技术作为数字化世界的核心&#xff0c;正在重塑着我们的生活方式、经济模式以及社会结构。在Web3时代&#xff0c;我们将目睹着一个以去中心化、透明化…

32.WEB渗透测试-数据传输与加解密(6)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;31.WEB渗透测试-数据传输与加解密&#xff08;5&#xff09; 关于discuz3.5的源码内容和…

小型社区与园区如何选购合适的停车场道闸系统?需注意什么

小型社区和园区停车场通常面临着空间有限、预算有限以及车流量相对较小的挑战。这些特点要求停车场的管理系统既要经济实用&#xff0c;又要能够满足基本的车辆管理需求&#xff0c;如安全性、通行效率和便捷性。针对这些特点&#xff0c;选择合适的停车道闸系统成为提高管理效…

天诚物联网锁亮相福州南京沈阳西安展会,与您见证AIoT行业发展

目光灼灼&#xff0c;步履不辍。自4月1日第七届CCLE中国教育后勤展览会一别&#xff0c;全场景AIoT解决方案服务商——江苏新巢天诚智能技术有限公司&#xff08;以下简称“天诚”&#xff09;打造的校园物联网锁软硬一体化解决方案获得了诸多准意向代理商、集成商同仁们的咨询…

2011年认证杯SPSSPRO杯数学建模B题(第二阶段)生物多样性的评估全过程文档及程序

2011年认证杯SPSSPRO杯数学建模 B题 生物多样性的评估 原题再现&#xff1a; 2010 年是联合国大会确定的国际生物多样性年。保护地球上的生物多样性已经越来越被人类社会所关注&#xff0c;相关的大规模科研和考察计划也层出不穷。为了更好地建立国际交流与专家间的合作&…

【网络安全】网络安全,你我同行——网络安全指南请查收~

网络是一把双刃剑&#xff0c;在给我们带来便捷生活的同时&#xff0c;也埋下了种种安全隐患。作为网络的亲身参与者&#xff0c;我们应该主动学习网络安全知识。快让我们一起来看看&#xff0c;本期安全小讲堂带来了哪些网络安全知识吧~ 01 常见网络安全隐患 网络安全问题无…

Docker镜像,什么是Docker镜像,Docker基本常用命令

docker镜像 1.1什么是镜像&#xff0c;镜像基础 1.1.1 镜像的简介 镜像是一种轻量级&#xff0c;可执行的独立软件包&#xff0c;也可以说是一个精简的操作系统。镜像中包含应用软件及应用软件的运行环境&#xff0c;具体来说镜像包含运行某个软件所需的所有内容&#xff0c;…

记一次逻辑漏洞拿下目标站点

开局某平台登录框 可做尝试手法 1、弱口令 2、万能密码 3、复杂密码逻辑绕过 4、登录框逻辑绕过 5、登录框注入 正文 某通用平台&#xff0c;系xxx科技公司开发全套模板通用系统&#xff0c;演示站踩点弱口令&#xff0c;这里主要梳理踩点思路 1、某接口未授权访问读系统用户…

前端Vue自定义勾选协议组件的开发与应用

摘要&#xff1a; 随着前端技术的不断发展&#xff0c;用户体验成为了软件开发中的关键要素。在登录、注册等场景中&#xff0c;勾选协议是常见的需求。本文旨在介绍一款基于 Vue.js 的自定义勾选协议组件的开发与应用&#xff0c;该组件适用于多种场景&#xff0c;并且具备良…