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

posted @ 2021-07-12 19:40  Yblue  阅读(4113)  评论(0编辑  收藏  举报