ThreadLocal在不同线程中的传递

1,468 阅读1分钟

在工作中遇到了一个ThreadLocal传值问题。因为公司业务有一个接口非常慢,所以想着使用异步的方法进行优化

但是因为业务中有redis分布式锁,而锁的值是用ThreadLocal来存储的,所以异步后会导致值丢失,锁无法释放

才有了本篇文章,研究ThreadLocal如何在不同线程中传递

首先来说一下线程传值的问题
现象解决方案介绍
同一线程之间传递值ThreadLocal给线程提供一个本地变量,当线程消失的时候,所有的本地示例都会被回收
父子线程之间传递值InheritableThreadLocaljdk提供的类,是ThreadLocal的升级版,解决父子线程之间传递值的问题
线程池不同线程传递值TransmittableThreadLocal简称TTL,是阿里巴巴团队提供的一个框架,主要解决因为存储线程池InheritableThreadLocal失效的问题
准备工作
  1. 首先引入依赖

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.13.RELEASE</version>
        </parent>
    
        <groupId>org.example</groupId>
        <artifactId>threadLocal-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>2.4.2</version>
            </dependency>
        </dependencies>
    </project>
    
  2. 创建TestController

    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    
    /**
     * @author by miao
     */
    @RestController
    public class TestController {
    
        @Resource
        private TestService testService;
    
        @RequestMapping("/test")
        public String test() {
            return testService.test();
        }
    }
    
  3. 创建TestService

    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    
    /**
     * @author miao
     */
    @Service
    public class TestService {
    
        public String test() {
    
            return "ok";
        }
    }
    
  4. 创建ThreadLocalUtil

    import java.util.UUID;
    
    /**
     * @author miao
     */
    public class ThreadLocalUtil {
    
        private static final ThreadLocal<String> LOCK_FLAG = new ThreadLocal<>();
    
        /**
         * 获取线程中保存的值
         *
         * @return String
         */
        public static String getValue() {
            return LOCK_FLAG.get();
        }
    
        /**
         * 设置线程需要保存的值
         *
         * @return String
         */
        public static String setValue() {
            String uuid = UUID.randomUUID().toString();
            LOCK_FLAG.set(uuid);
            return uuid;
        }
    
        /**
         * 移除线程中保存的值
         */
        public static void removeValue() {
            LOCK_FLAG.remove();
        }
    
    }
    

1. 同一线程内传值

1)TestServicetest()

    public String test() {

        System.out.println("+++++++++++++++"+ThreadLocalUtil.setValue());

        System.out.println("业务逻辑处理");
        System.out.println("---------------" + ThreadLocalUtil.getValue());
        return "ok";
    }
  1. 浏览器访问:http://localhost:8080/test

​ 结果为:

ThreadLocal同一线程内传值

2. 父子线程内传值

  1. 在启动类中,增加开启异步支持,增加注解@EnableAsync
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

/**
 * @author by 15888
 * @date 2021/4/21 17:34
 */
@EnableAsync
@SpringBootApplication
public class ThreadLocalApplication {
    public static void main(String[] args) {
        SpringApplication.run(ThreadLocalApplication.class, args);

    }
}
  1. 创建AysncService
/**
 * @author miao
 */
@Service
public class AsyncService {

    @Async
    public void getThreadLocalValue() {

        System.out.println("+++++++++++++++" + ThreadLocalUtil.getValue());
    }
}
  1. TestService中使用AsyncService
    
    @Resource
    private AsyncService asyncService;

	public String test1() {

        System.out.println("+++++++++++++++"+ThreadLocalUtil.setValue());

        System.out.println("业务逻辑处理");
        asyncService.getThreadLocalValue();
        return "ok";
    }
  1. ThreadLocalUtil中的LOCK_FLAG实现类改为 InheritableThreadLocal
private static final ThreadLocal<String> LOCK_FLAG = new InheritableThreadLocal<>();

5)浏览器访问:http://localhost:8080/test

结果为:ThreadLocal父子线程内传值

关于父子线程之间的传值问题解决请移步我的第二篇文章:《ThreadLocal异步线程池传值》