springboot中如何使用ThreadLocal?
ThreadLocal的作用:用来存当前线程的局部变量,不同线程间互不干扰。拿完数据记得需要移除数据,不然JVM不会将ThreadLocal回收(可能还会被引用),多了就会出现内存泄漏的情况。
springboot中如何使用ThreadLocal?
其实很简单,就是将ThreadLocal变成一个bean(也就是初始化ThreadLocal<T>),在不同层间用同一个对象就行。
写一个小demo。
1.初始化ThreadLocal
package com.yblue.config; import org.springframework.context.annotation.Scope; 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回收(可能还会被引用),多了就会出现内存泄漏的情况 } }
效果如下,一样不会影响。
想看更多精彩内容,可以关注我的CSDN