1. ThreadLocal内存泄漏
实线代表强引用,虚线代表弱引用
每一个Thread维护一个ThreadLocalMap, key为使用弱引用的ThreadLocal实例,value为线程变量的副本。
强引用,使用最普遍的引用,一个对象具有强引用,不会被垃圾回收器回收。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不回收这种对象。
如果想取消强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样可以使JVM在合适的时间就会回收该对象。
弱引用,JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。
2. 文章详情
2.1 接口说明
接口url:/articles/view/{id}
请求方式:POST
请求参数:
参数名称 | 参数类型 | 说明 |
---|---|---|
id | long | 文章id(路径参数) |
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": {
"id": 1,
"title": "springboot介绍以及入门案例",
"summary": "介绍",
"commentCounts": 0,
"viewCounts": 60,
"weight": 0,
"createDate": "2609-06-26 15:58",
"author": "李四",
"body": {
"content": "111"
},
"tags": [
{
"id": 5,
"tagName": "springboot"
},
{
"id": 7,
"tagName": "springmvc"
},
{
"id": 8,
"tagName": "11"
}
],
"category": {
"id": 2,
"avatar": "/category/back.png",
"categoryName": "后端"
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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;
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
package com.mszlu.blog.dao.pojo;
import lombok.Data;
@Data
public class ArticleBody {
private Long id;
private String content;
private String contentHtml;
private Long articleId;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
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;
1
2
3
4
5
6
7
2
3
4
5
6
7
package com.mszlu.blog.dao.pojo;
import lombok.Data;
@Data
public class Category {
private Long id;
private String avatar;
private String categoryName;
private String description;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2.3 Controller
@PostMapping("view/{id}")
public Result findArticleById(@PathVariable("id") Long id) {
ArticleVo articleVo = articleService.findArticleById(id);
return Result.success(articleVo);
}
1
2
3
4
5
6
2
3
4
5
6
2.4 Service
ArticleVo findArticleById(Long id);
1
@Override
public ArticleVo findArticleById(Long id) {
Article article = articleMapper.selectById(id);
return copy(article,true,true,true,true);
}
1
2
3
4
5
6
7
2
3
4
5
6
7
package com.mszlu.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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
ArticleVo中的属性填充:
private List<ArticleVo> copyList(List<Article> records, boolean isTag, boolean isAuthor) {
List<ArticleVo> articleVoList = new ArrayList<>();
for (Article record : records) {
articleVoList.add(copy(record,isTag,isAuthor,false,false));
}
return articleVoList;
}
private List<ArticleVo> copyList(List<Article> records, boolean isTag, boolean isAuthor,boolean isBody) {
List<ArticleVo> articleVoList = new ArrayList<>();
for (Article record : records) {
articleVoList.add(copy(record,isTag,isAuthor,isBody,false));
}
return articleVoList;
}
private List<ArticleVo> copyList(List<Article> records, boolean isTag, boolean isAuthor,boolean isBody,boolean isCategory) {
List<ArticleVo> articleVoList = new ArrayList<>();
for (Article record : records) {
articleVoList.add(copy(record,isTag,isAuthor,isBody,isCategory));
}
return articleVoList;
}
private ArticleVo copy(Article article, boolean isTag, boolean isAuthor, boolean isBody, boolean isCategory){
ArticleVo articleVo = new ArticleVo();
BeanUtils.copyProperties(article,articleVo);
articleVo.setCreateDate(new DateTime(article.getCreateDate()).toString("yyyy-MM-dd HH:mm"));
//并不是所有的接口 都需要标签 ,作者信息
if (isTag){
Long articleId = article.getId();
articleVo.setTags(tagService.findTagsByArticleId(articleId));
}
if (isAuthor){
Long authorId = article.getAuthorId();
articleVo.setAuthor(sysUserService.findUserById(authorId).getNickname());
}
if (isBody){
ArticleBodyVo articleBody = findArticleBody(article.getId());
articleVo.setBody(articleBody);
}
if (isCategory){
CategoryVo categoryVo = findCategory(article.getCategoryId());
articleVo.setCategory(categoryVo);
}
return articleVo;
}
@Autowired
private CategoryService categoryService;
private CategoryVo findCategory(Long categoryId) {
return categoryService.findCategoryById(categoryId);
}
@Autowired
private ArticleBodyMapper articleBodyMapper;
private ArticleBodyVo findArticleBody(Long articleId) {
LambdaQueryWrapper<ArticleBody> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ArticleBody::getArticleId,articleId);
ArticleBody articleBody = articleBodyMapper.selectOne(queryWrapper);
ArticleBodyVo articleBodyVo = new ArticleBodyVo();
articleBodyVo.setContent(articleBody.getContent());
return articleBodyVo;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.mszlu.blog.vo;
import lombok.Data;
@Data
public class CategoryVo {
private Long id;
private String avatar;
private String categoryName;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.mszlu.blog.vo;
import lombok.Data;
@Data
public class ArticleBodyVo {
private String content;
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
package com.mszlu.blog.service;
import com.mszlu.blog.vo.CategoryVo;
public interface CategoryService {
CategoryVo findCategoryById(Long id);
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
package com.mszlu.blog.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mszlu.blog.dao.mapper.CategoryMapper;
import com.mszlu.blog.dao.pojo.Category;
import com.mszlu.blog.service.CategoryService;
import com.mszlu.blog.vo.CategoryVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CategoryServiceImpl implements CategoryService {
@Autowired
private CategoryMapper categoryMapper;
@Override
public CategoryVo findCategoryById(Long id){
Category category = categoryMapper.selectById(id);
CategoryVo categoryVo = new CategoryVo();
BeanUtils.copyProperties(category,categoryVo);
return categoryVo;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.mszlu.blog.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mszlu.blog.dao.pojo.ArticleBody;
public interface ArticleBodyMapper extends BaseMapper<ArticleBody> {
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
package com.mszlu.blog.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mszlu.blog.dao.pojo.Category;
public interface CategoryMapper extends BaseMapper<Category> {
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
2.5 测试
3. 使用线程池 更新阅读次数
3.1 线程池配置
package com.mszlu.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("码神之路博客项目");
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
//执行初始化
executor.initialize();
return executor;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
3.1 使用
package com.mszlu.blog.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mszlu.blog.dao.mapper.ArticleMapper;
import com.mszlu.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 updateViewCount(ArticleMapper articleMapper,Article article){
Article articleUpdate = new Article();
articleUpdate.setViewCounts(article.getViewCounts() + 1);
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Article::getId,article.getId());
queryWrapper.eq(Article::getViewCounts,article.getViewCounts());
articleMapper.update(articleUpdate,queryWrapper);
try {
//睡眠5秒 证明不会影响主线程的使用
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Autowired
private ThreadService threadService;
@Override
public ArticleVo findArticleById(Long id) {
Article article = articleMapper.selectById(id);
threadService.updateViewCount(articleMapper,article);
return copy(article,true,true,true,true);
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
3.3 测试
睡眠 ThredService中的方法 5秒,不会影响主线程的使用,即文章详情会很快的显示出来,不受影响