ThreadLocal的作用:用来存当前线程的局部变量,不同线程间互不干扰。拿完数据记得需要移除数据,不然JVM不会将ThreadLocal回收(可能还会被引用),多了就会出现内存泄漏的情况。
springboot中如何使用ThreadLocal?
其实很简单,就是将ThreadLocal变成一个bean(也就是初始化ThreadLocal),在不同层间用同一个对象就行。
写一个小demo。
1.初始化ThreadLocal
package com.yblue.config;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author JiaXinMa
* @description 使用ThreadLocal
* @date 2021/7/12
*/
@Component
public class ThreadLocalConfig {
// jdk建议将 ThreadLocal 定义为 private static ,这样就不会有弱引用,内存泄漏的问题了
private static ThreadLocal<Map> mapThreadLocal = new ThreadLocal<>();
//获取当前线程的存的变量
public Map get() {
return mapThreadLocal.get();
}
//设置当前线程的存的变量
public void set(Map map) {
this.mapThreadLocal.set(map);
}
//移除当前线程的存的变量
public void remove() {
this.mapThreadLocal.remove();
}
}
2.控制层
package com.yblue.controller;
import com.yblue.config.ThreadLocalConfig;
import com.yblue.service.ThreadLocalTestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* @author JiaXinMa
* @description 测试ThreadLocal
* @date 2021/7/12
*/
@Slf4j
@RestController
public class ThreadLocalTestController {
@Autowired
ThreadLocalTestService threadLocalTestService;
@Autowired
ThreadLocalConfig threadLocalConfig;
//@Component生成的bean默认是单例的,
// 那可能高并发的时候两个用户同时传参过来,可能存在修改了当前这个数据
@GetMapping("/threadLocal")
public void test(String userId) {
log.info("/threadLocal:{}", userId);
Map map = new HashMap<String, String>();
map.put("userId", userId);
threadLocalConfig.set(map);
//休眠5秒测试不同线程会不会修改同一个对象threadLocalConfig的值
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程:" + Thread.currentThread().getName() +"从控制层即将进入业务层");
threadLocalTestService.threadLocalTest();
}
}
3.业务层
package com.yblue.service;
import com.yblue.config.ThreadLocalConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @author JiaXinMa
* @description 测试ThreadLocal
* @date 2021/7/12
*/
@Service
public class ThreadLocalTestService {
@Autowired
ThreadLocalConfig threadLocalConfig;
public void threadLocalTest() {
Map map = threadLocalConfig.get();
System.out.println("当前线程:" + Thread.currentThread().getName() + "的用户id:" + map.get("userId"));
threadLocalConfig.remove();//拿完数据记得需要移除数据,不然JVM不会将ThreadLocal回收(可能还会被引用),多了就会出现内存泄漏的情况
}
}
效果如下:
在测试类里模拟上述情况一下:
package cn.mindgd.test;
import cn.mindgd.config.ThreadLocalConfig;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest
public class TestThreadLocal {
@Autowired
ThreadLocalConfig threadLocalConfig;
@Test
public void tesThreadLocalConfig() {
Map map = new HashMap<String, String>();
map.put("userId", "1");
threadLocalConfig.set(map);
new Thread(new Runnable() {
@Override
public void run() {
Map map = new HashMap<String, String>();
map.put("userId", "2");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadLocalConfig.set(map);
Object userId = threadLocalConfig.get().get("userId");
System.out.println("当前线程:" + Thread.currentThread().getName() + "的用户id:" + map.get("userId"));
threadLocalConfig.remove();
}
}).start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程:" + Thread.currentThread().getName() + "的用户id:" + map.get("userId"));
threadLocalConfig.remove();//拿完数据记得需要移除数据,不然JVM不会将ThreadLocal回收(可能还会被引用),多了就会出现内存泄漏的情况
}
}
效果如下,一样不会影响。
想看更多精彩内容,可以关注我的博客园
我的博客园