ThreadLocal和InheritableThreadLocal
- ThreadLocal是线程上下文,但是当前线程如果创建子线程,那么子线程无法获取到值
public class Test {
static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
threadLocal.set(1);
System.out.println("当前线程变量:" + threadLocal.get());
new Thread(() -> System.out.println("当前线程变量:" + threadLocal.get())).start();
}
}
- 这种机制本身没有错误,线程里创建了新线程,本来就是不一样的上下文。那如果我们需要让子线程也继承上下文你的值呢?InheritableThreadLocal派上用场
public class Test {
static InheritableThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) {
threadLocal.set(1);
System.out.println("当前线程变量:" + threadLocal.get());
new Thread(() -> System.out.println("当前线程变量:" + threadLocal.get())).start();
}
}
- 但是InheritableThreadLocal还是无法满足所有场景,比如线程池的情况,里面的线程可是复用的,那个上下文会被不断重复使用,这不是我们需要的。那么能解决这种问题的就是:transmittable-thread-local
transmittable-thread-local
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.12.3</version>
</dependency>
static TransmittableThreadLocal<Integer> threadLocal = new TransmittableThreadLocal<>();
public static void main(String[] args) {
example1();
example2();
}
public static void example1() {
threadLocal.set(1);
System.out.println("当前线程变量:" + threadLocal.get());
new Thread(() -> System.out.println("当前线程变量:" + threadLocal.get())).start();
}
}
public static void example2() {
int count = 10;
ExecutorService executorService = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(2));
while (count-- > 0) {
threadLocal.set(count);
System.out.println("当前线程变量:" + threadLocal.get());
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("线程id:" + Thread.currentThread().getId() + ",线程池-线程变量:" + threadLocal.get());
}
});
}
}
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.6.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
@Slf4j
@Component
public class AsyncTask {
@Async
public void asyncRun() {
log.info("asyncRun: 线程id:{},当前trace_id:{}", Thread.currentThread().getId(), App.threadLocal.get());
}
}
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
return new ThreadPoolExecutor(2, 2, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100));
}
}
@SpringBootApplication
@EnableScheduling
@EnableAsync
@Slf4j
public class App {
public static TransmittableThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
@Autowired
AsyncTask asyncTask;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
@Scheduled(fixedRate = 2000L)
public void schedule() {
String traceId = UUID.randomUUID().toString();
threadLocal.set(traceId);
log.info("定时任务线程id:{},当前trace_id:{}", Thread.currentThread().getId(), threadLocal.get());
asyncTask.asyncRun();
}
}
// 以下是console输出(可以发现两条线程的traceid一直维持最初设置的值了)
2021-12-25 15:56:59.402 INFO 15480 --- [ scheduling-1] org.example.App : 定时任务线程id:24,当前trace_id:5af5e4ac-b911-45d4-8554-3a4f4ad13aa4
2021-12-25 15:56:59.407 INFO 15480 --- [ main] org.example.App : Started App in 1.454 seconds (JVM running for 2.926)
2021-12-25 15:56:59.471 INFO 15480 --- [pool-1-thread-1] org.example.aysnc.AsyncTask : asyncRun: 线程id:25,当前trace_id:5af5e4ac-b911-45d4-8554-3a4f4ad13aa4
2021-12-25 15:57:01.416 INFO 15480 --- [ scheduling-1] org.example.App : 定时任务线程id:24,当前trace_id:f40a801a-45a0-4ec9-9c7a-97d2536bdaac
2021-12-25 15:57:01.417 INFO 15480 --- [pool-1-thread-2] org.example.aysnc.AsyncTask : asyncRun: 线程id:32,当前trace_id:f40a801a-45a0-4ec9-9c7a-97d2536bdaac
2021-12-25 15:57:03.411 INFO 15480 --- [ scheduling-1] org.example.App : 定时任务线程id:24,当前trace_id:77ea9d4f-3848-4316-bb6e-0216a43e5ff7
2021-12-25 15:57:03.411 INFO 15480 --- [pool-1-thread-1] org.example.aysnc.AsyncTask : asyncRun: 线程id:25,当前trace_id:5af5e4ac-b911-45d4-8554-3a4f4ad13aa4
2021-12-25 15:57:05.413 INFO 15480 --- [ scheduling-1] org.example.App : 定时任务线程id:24,当前trace_id:4e439446-084b-4e41-812b-bf545131abfd
2021-12-25 15:57:05.413 INFO 15480 --- [pool-1-thread-2] org.example.aysnc.AsyncTask : asyncRun: 线程id:32,当前trace_id:f40a801a-45a0-4ec9-9c7a-97d2536bdaac
2021-12-25 15:57:07.410 INFO 15480 --- [ scheduling-1] org.example.App : 定时任务线程id:24,当前trace_id:0ce9642d-048a-4e51-9cb3-5101a19ddae0
2021-12-25 15:57:07.410 INFO 15480 --- [pool-1-thread-1] org.example.aysnc.AsyncTask : asyncRun: 线程id:25,当前trace_id:5af5e4ac-b911-45d4-8554-3a4f4ad13aa4
2021-12-25 15:57:09.409 INFO 15480 --- [ scheduling-1] org.example.App : 定时任务线程id:24,当前trace_id:df5f8369-9421-4034-9f76-1dbe3f8d6e21
2021-12-25 15:57:09.410 INFO 15480 --- [pool-1-thread-2] org.example.aysnc.AsyncTask : asyncRun: 线程id:32,当前trace_id:f40a801a-45a0-4ec9-9c7a-97d2536bdaac
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100));
return TtlExecutors.getTtlExecutor(threadPoolExecutor);
}
}
//console输出如下
2021-12-25 16:01:17.841 INFO 4428 --- [ scheduling-1] org.example.App : 定时任务线程id:24,当前trace_id:8b17bd65-edb5-4d10-b630-15ba89764ff8
2021-12-25 16:01:17.844 INFO 4428 --- [ main] org.example.App : Started App in 1.45 seconds (JVM running for 2.875)
2021-12-25 16:01:17.917 INFO 4428 --- [pool-1-thread-1] org.example.aysnc.AsyncTask : asyncRun: 线程id:26,当前trace_id:8b17bd65-edb5-4d10-b630-15ba89764ff8
2021-12-25 16:01:19.854 INFO 4428 --- [ scheduling-1] org.example.App : 定时任务线程id:24,当前trace_id:df551e9f-0eec-432b-af92-009b8032b4db
2021-12-25 16:01:19.855 INFO 4428 --- [pool-1-thread-2] org.example.aysnc.AsyncTask : asyncRun: 线程id:32,当前trace_id:df551e9f-0eec-432b-af92-009b8032b4db
2021-12-25 16:01:21.854 INFO 4428 --- [ scheduling-1] org.example.App : 定时任务线程id:24,当前trace_id:cac54b9b-c1ef-46d4-8c89-422316bc59f7
2021-12-25 16:01:21.854 INFO 4428 --- [pool-1-thread-1] org.example.aysnc.AsyncTask : asyncRun: 线程id:26,当前trace_id:cac54b9b-c1ef-46d4-8c89-422316bc59f7
2021-12-25 16:01:23.853 INFO 4428 --- [ scheduling-1] org.example.App : 定时任务线程id:24,当前trace_id:69875240-9745-41b5-831b-66ddfbca2ea7
2021-12-25 16:01:23.853 INFO 4428 --- [pool-1-thread-2] org.example.aysnc.AsyncTask : asyncRun: 线程id:32,当前trace_id:69875240-9745-41b5-831b-66ddfbca2ea7
2021-12-25 16:01:25.848 INFO 4428 --- [ scheduling-1] org.example.App : 定时任务线程id:24,当前trace_id:b81db337-818d-40ed-929b-971ce460d5c2
2021-12-25 16:01:25.848 INFO 4428 --- [pool-1-thread-1] org.example.aysnc.AsyncTask : asyncRun: 线程id:26,当前trace_id:b81db337-818d-40ed-929b-971ce460d5c2
2021-12-25 16:01:27.846 INFO 4428 --- [ scheduling-1] org.example.App : 定时任务线程id:24,当前trace_id:c59338a3-d981-4ca2-a8a9-7ba6815165cb
2021-12-25 16:01:27.847 INFO 4428 --- [pool-1-thread-2] org.example.aysnc.AsyncTask : asyncRun: 线程id:32,当前trace_id:c59338a3-d981-4ca2-a8a9-7ba6815165cb
结尾
- 简单对比了几个ThreadLocal的区别,下一篇文章会对其工作原理做解析
- 大家有什么问题可以留言,如果觉得我写得可以,请给我一个赞!谢谢,你们的支持是我不断更新的动力,我还有其他专栏大家可以看看(xxl-job,feign等),后续还会更新各种框架的使用和原理解析!