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