在工作中遇到了一个ThreadLocal传值问题。因为公司业务有一个接口非常慢,所以想着使用异步的方法进行优化
但是因为业务中有redis分布式锁,而锁的值是用ThreadLocal来存储的,所以异步后会导致值丢失,锁无法释放
才有了本篇文章,研究ThreadLocal如何在不同线程中传递
首先来说一下线程传值的问题
现象 | 解决方案 | 介绍 |
---|---|---|
同一线程之间传递值 | ThreadLocal | 给线程提供一个本地变量,当线程消失的时候,所有的本地示例都会被回收 |
父子线程之间传递值 | InheritableThreadLocal | jdk提供的类,是ThreadLocal的升级版,解决父子线程之间传递值的问题 |
线程池不同线程传递值 | TransmittableThreadLocal | 简称TTL,是阿里巴巴团队提供的一个框架,主要解决因为存储线程池InheritableThreadLocal 失效的问题 |
准备工作
-
首先引入依赖
<?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>
-
创建
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(); } }
-
创建
TestService
import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * @author miao */ @Service public class TestService { public String test() { return "ok"; } }
-
创建
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)TestService
的test()
public String test() {
System.out.println("+++++++++++++++"+ThreadLocalUtil.setValue());
System.out.println("业务逻辑处理");
System.out.println("---------------" + ThreadLocalUtil.getValue());
return "ok";
}
- 浏览器访问:
http://localhost:8080/test
结果为:
2. 父子线程内传值
- 在启动类中,增加开启异步支持,增加注解
@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);
}
}
- 创建
AysncService
/**
* @author miao
*/
@Service
public class AsyncService {
@Async
public void getThreadLocalValue() {
System.out.println("+++++++++++++++" + ThreadLocalUtil.getValue());
}
}
- 在
TestService
中使用AsyncService
@Resource
private AsyncService asyncService;
public String test1() {
System.out.println("+++++++++++++++"+ThreadLocalUtil.setValue());
System.out.println("业务逻辑处理");
asyncService.getThreadLocalValue();
return "ok";
}
ThreadLocalUtil
中的LOCK_FLAG
实现类改为InheritableThreadLocal
private static final ThreadLocal<String> LOCK_FLAG = new InheritableThreadLocal<>();
5)浏览器访问:http://localhost:8080/test
结果为:
关于父子线程之间的传值问题解决请移步我的第二篇文章:《ThreadLocal异步线程池传值》