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

我的CSDN


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM