✏️
blog
  • README
  • 2023 11
    • expect使用
  • 2023 10
    • 通过Appium给iOS应用自动化执行脚本
  • 2023 06
    • 三种ThreadLocal详解
    • 常见限流算法总结
    • 分布式ID生成算法
  • 2023 05
    • 线上机器CLOSE_WAIT连接数异常排查
    • 多数据源引发transactional事务回滚失效
  • 2023 04
    • MySQL中BufferPool
  • 2022 12
    • Linux IO
    • Netty总结
  • 2022 04
    • Thrift
  • 2022 03
    • JVM命令总结
    • 频繁FullGC定位思路
    • Redis总结
    • Spring常见问题总结
    • Kafka总结
  • 2022 02
    • Dubbo柔性服务天池大赛总结
  • 2021 12
    • 泛型中的extends和super
    • 手写一个Spring Boot Starter
  • 2021 11
    • 常用消息队列总结
  • 2021 10
    • swagger2快速使用
    • SpringBoot接口cors跨域访问
  • 2021 08
    • 常用shell命令总结
  • 2021 05
    • 线程cpu飙升排查
    • zookeeper install
  • 2021 04
    • Java虚拟机
    • [Spring Boot](2021-04/2021-04-04-Spring Boot.md)
    • [Spring MVC](2021-04/2021-04-04-Spring MVC.md)
    • 分布式ID
    • 消息队列
    • [Spring AOP](2021-04/2021-04-05-Spring AOP.md)
    • 布隆过滤器
    • Scala内核Spark阻塞排查
  • 2020 12
    • 使用Python优雅实现tail命令
  • 2020 11
    • Spark基础架构
    • 一文搞定Git
    • Spark线上问题引发的思考
  • 2020 04
    • 使用GitBook
  • 2019 05
    • SELinux、Netfilter、iptables、firewall和ufw五者关系
    • 安装npm和nodejs
    • 访问不到云服务器中的项目
  • 2019 04
    • 二叉树中节点与度数
    • 实现会话跟踪的技术有哪些
    • 计算机操作系统-死锁
    • Semaphore Count Down Latch Cyclic Barrier
    • Java内存模型
    • 双重检查锁定
    • synchronized实现底层
    • Lock接口
    • HTTP与HTTPS的区别
    • Java中线程池
    • Java中的阻塞队列
    • 排序算法
  • 2019 03
    • MySQL中索引
    • MySQL存储引擎
    • MySQL锁机制
    • n的阶乘结果后面0的个数
由 GitBook 提供支持
在本页
  • ThreadLocal
  • InheritableThreadLocal
  • TransmittableThreadLocal
  • 代码实验示例

这有帮助吗?

  1. 2023 06

三种ThreadLocal详解

ThreadLocal在实际开发中经常被用于存储当前线程上下文信息,如登录用户信息、全局traceId以及一些需要线程间隔离的数据

这篇文章主要是针对ThreadLocal的问题,进行简要分析,以及延伸出来的另外两种跨线程/线程池ThreadLocal简单介绍

ThreadLocal

在Thread对象中有字段 threadLocals(ThreadLocalMap) 用于存储 key(ThreadLocal对象,弱引用) value(ThreadLocal value) 这个map通过线性探测法解决hash冲突问题,每次get/set时可能还伴随着清理掉key为null的entry键值对 主要问题:

  1. 当前线程的ThreadLocal无法透传到子线程或线程池中

  2. 当使用ThreadLocal对象时没有显式remove时,可能导致内存泄露

InheritableThreadLocal

InheritableThreadLocal 子线程在创建时会拷贝父线程中的inheritableThreadLocalMap Entry会重新创建,并插入子线程inheritableThreadLocalMap中,value只是浅拷贝

存在的主要问题:

  1. value为浅拷贝,如果value为对象,在子线程中对value的属性的修改,父线程中也会对应改变

  2. 拷贝过程只会发生在线程创建初始化时,当子线程如果是在线程池中,如果线程复用不会销毁,所以只有在首次创建线程 时才会透传,当线程已全部创建完毕时,InheritableThreadLocal是无法进行透传到线程池中的线程的 代码输出

TransmittableThreadLocal

如果需要将ThreadLocal对象透传到线程池Runnable中使用,需要使用TransmittableThreadLocal

且需要配合TtlRunnable/TtlCallable使用,在原本Runnable中wrap一层

代码实验示例

package com.passer.threadlocal;

import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.TtlRunnable;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author passer
 * @time 2023/6/11 12:13
 */
public class ThreadLocalDemo {
    public static void main(String[] args) throws Exception {
        testInheritableThreadLocal();
//        testTransmittableThreadLocal();
    }

    /**
     * <pre>
     * 在Thread对象中有字段 threadLocals(ThreadLocalMap) 用于存储 key(ThreadLocal对象,弱引用) value(ThreadLocal value)
     * 这个map通过线性探测法解决hash冲突问题,每次get/set时可能还伴随着清理掉key为null的entry键值对
     * 主要问题:
     * 1. 当前线程的ThreadLocal无法透传到子线程或线程池中
     * 2. 当使用ThreadLocal对象时没有显式remove时,可能导致内存泄露
     * </pre>
     */
    public static void testThreadLocal() {
        ThreadLocal<String> threadLocal = new ThreadLocal<>();
        threadLocal.set("hello");

        System.out.println(threadLocal.get());
    }

    /**
     * <pre>
     * 如果需要将ThreadLocal对象透传到线程池Runnable中使用,需要使用TransmittableThreadLocal
     * 且需要配合TtlRunnable/TtlCallable使用,在原本Runnable中wrap一层
     *
     * 代码输出
     * =================================
     * class com.alibaba.ttl.TransmittableThreadLocal TEST
     * pool-1-thread-1 --> class com.alibaba.ttl.TransmittableThreadLocal TEST, threadName: main
     * set local pool-1-thread-1
     * pool-1-thread-2 --> class com.alibaba.ttl.TransmittableThreadLocal TEST, threadName: main
     * set local pool-1-thread-2
     * pool-1-thread-3 --> class com.alibaba.ttl.TransmittableThreadLocal TEST, threadName: main
     * set local pool-1-thread-3
     * ==================
     * main --> class com.alibaba.ttl.TransmittableThreadLocal TEST, threadName: main
     * ==================
     * pool-1-thread-1 --> class com.alibaba.ttl.TransmittableThreadLocal TEST, threadName: main
     * pool-1-thread-1 --> class com.alibaba.ttl.TransmittableThreadLocal TEST, threadName: main
     * pool-1-thread-2 --> class com.alibaba.ttl.TransmittableThreadLocal TEST, threadName: main
     * </pre>
     */
    public static void testTransmittableThreadLocal() throws Exception {
        TransmittableThreadLocal<String> transmittableThreadLocal = new TransmittableThreadLocal<>();
        testThreadLocal(transmittableThreadLocal);
    }


    /**
     * <pre>    
     * InheritableThreadLocal 子线程在创建时会拷贝父线程中的inheritableThreadLocalMap
     * Entry会重新创建,并插入子线程inheritableThreadLocalMap中,value只是浅拷贝
     * 存在的主要问题:
     * 1. value为浅拷贝,如果value为对象,在子线程中对value的属性的修改,父线程中也会对应改变
     * 2. 拷贝过程只会发生在线程创建初始化时,当子线程如果是在线程池中,如果线程复用不会销毁,所以只有在首次创建线程
     * 时才会透传,当线程已全部创建完毕时,InheritableThreadLocal是无法进行透传到线程池中的线程的
     * 代码输出
     * =================================
     * class java.lang.InheritableThreadLocal TEST 
     * pool-1-thread-1 --> class java.lang.InheritableThreadLocal TEST, threadName: main
     * set local pool-1-thread-1
     * pool-1-thread-2 --> class java.lang.InheritableThreadLocal TEST, threadName: main
     * set local pool-1-thread-2
     * pool-1-thread-3 --> class java.lang.InheritableThreadLocal TEST, threadName: main
     * set local pool-1-thread-3
     * ==================
     * main --> class java.lang.InheritableThreadLocal TEST, threadName: main
     * ==================
     * pool-1-thread-1 --> set new TEST, threadName: pool-1-thread-1
     * pool-1-thread-1 --> set new TEST, threadName: pool-1-thread-1
     * pool-1-thread-2 --> set new TEST, threadName: pool-1-thread-2
     * </pre>
     */
    public static void testInheritableThreadLocal() throws Exception{
        InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
        testThreadLocal(inheritableThreadLocal);
    }

    public static void testThreadLocal(ThreadLocal threadLocal) throws Exception{
        System.out.println("=================================");
        System.out.println(threadLocal.getClass() + " TEST ");
        CountDownLatch countDownLatch1 = new CountDownLatch(3);
        threadLocal.set(threadLocal.getClass() + " TEST, threadName: " + Thread.currentThread().getName());
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 3; i++) {
            executorService.submit(() -> {
                System.out.println(Thread.currentThread().getName() + " --> " + threadLocal.get());
                threadLocal.set("set new TEST, threadName: " + Thread.currentThread().getName());
                System.out.println("set local " + Thread.currentThread().getName());
                countDownLatch1.countDown();
            });
        }
        countDownLatch1.await();

        System.out.println("==================");
        System.out.println(Thread.currentThread().getName() + " --> " + threadLocal.get());
        System.out.println("==================");

        CountDownLatch countDownLatch2 = new CountDownLatch(3);
        for (int i = 0; i < 3; i++) {
            executorService.submit(TtlRunnable.get(() -> {
                System.out.println(Thread.currentThread().getName() + " --> " + threadLocal.get());
                countDownLatch2.countDown();
            }));
        }
        countDownLatch2.await();
        executorService.shutdown();
    }
}
上一页2023 06下一页常见限流算法总结

最后更新于1年前

这有帮助吗?