ThreadLocal值传递问题_threadlocal传递参数

}


主线程中定义一个静态变量util,初始值是123,在子线程和主线程中分别打印ThreadLocal变量值。



public class ThreadLocalExample {
private static Logger logger = LoggerFactory.getLogger(ThreadLocalExample.class);
private static ThreadLocalUtil util = new ThreadLocalUtil<>(123);

public static void main(String[] args) {
    new Thread(() -> logger.info("Thread name = {}, thread variable is {}", Thread.currentThread().getName(), util.getValue())).start();
    logger.info("Thread name = {}, thread variable is {}", Thread.currentThread().getName(), util.getValue());
}

}


示例结果如下,子线程输出的是子线程初始值null,主线程输出的是初始值123。另外,这里**不保证线程的有序性**,只是简易示例。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/341bd44e08284388971f5b61448978f8.png)


## InheritableThreadLocal(ITL)示例


InheritableThreadLocal主要用于子线程创建时,需要自动继承父线程的ThreadLocal变量时使用。  
 把上面的代码稍微改造一下,



/**
* ThreadLocal工具类
*
* @param 范型T
*/
public final class ThreadLocalUtil {
private final ThreadLocal THREAD_LOCAL = new InheritableThreadLocal<>();

/\*\*

* 无参构造函数
*/
public ThreadLocalUtil() {
}

/\*\*

* 有参构造函数
*
* @param t
*/
ThreadLocalUtil(T t) {
THREAD_LOCAL.set(t);
}

/\*\*

* 从ThreadLocalMap中获取当前线程变量
*
* @return 变量值
*/
public T getValue() {
return THREAD_LOCAL.get();
}

/\*\*

* ThreadLocalMap中写入当前线程变量
*
* @param t 入参
* @return 当前写入值
*/
public T setValue(T t) {
THREAD_LOCAL.set(t);
return t;
}

/\*\*

* 从ThreadLocalMap中删除当前线程变量
*/
public void removeValue() {
THREAD_LOCAL.remove();
}
}


代码不变,子线程中输出的ThreadLocal变量继承了主线程中的ThreadLocal变量值,  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/f2834b0dc2a54c8e9eb1532c863ead70.png)


## TransmittableThreadLocal(TTL)示例


TransmittableThreadLocal,简称TTL,它不同于ITL,它能实现池化线程间传递。直接看官方的时序图,  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/b2a3c358036b4bf195ddd8894eca63e1.png)  
 大部分过程都依赖于 TransmittableThreadLocal 或 TransmittableThreadLocal 中声明的静态工具类 Transmitter 。Transmitter 主要负责 ThreadLocal 的管理和值的传递。


使用前需要导入依赖,



    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>transmittable-thread-local</artifactId>
        <version>2.12.6</version>
    </dependency>

下面启了两个线程main\_01和main\_02作为父线程,线程池中执行的三个子线程拿到父线程设置的ttl变量,父线程再修改ttl值,然后线程池中的子线程继续读取ttl中的值。



import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;

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

public class TransmittableThreadLocalDemo {
/**
* 需要注意的是,使用TTL的时候,要想传递的值不出问题,线程池必须得用TTL加一层代理
*/
private static ExecutorService executorService = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(2));
private static ThreadLocal ttl = new TransmittableThreadLocal<>();

public static void main(String[] args) {
    new Thread(() -> {
        String mainThreadName = "main\_01";
        ttl.set(1);
        executorService.execute(() -> {
            sleep(1L);
            System.out.println(String.format("本地变量改变之前(1), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
        });

        executorService.execute(() -> {
            sleep(1L);
            System.out.println(String.format("本地变量改变之前(1), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
        });

        executorService.execute(() -> {
            sleep(1L);
            System.out.println(String.format("本地变量改变之前(1), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
        });

        sleep(1L); //确保上面的会在tl.set执行之前执行
        ttl.set(2); // 等上面的线程池第一次启用完了,父线程再给自己赋值

        executorService.execute(() -> {
            sleep(1L);
            System.out.println(String.format("本地变量改变之后(2), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
        });

        executorService.execute(() -> {
            sleep(1L);
            System.out.println(String.format("本地变量改变之后(2), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
        });

        executorService.execute(() -> {
            sleep(1L);
            System.out.println(String.format("本地变量改变之后(2), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
        });

        System.out.println(String.format("线程名称-%s, 变量值=%s", Thread.currentThread().getName(), ttl.get()));

    }).start();


    new Thread(() -> {

        String mainThreadName = "main\_02";

        ttl.set(3);

        executorService.execute(() -> {
            sleep(1L);
            System.out.println(String.format("本地变量改变之前(3), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
        });

        executorService.execute(() -> {
            sleep(1L);
            System.out.println(String.format("本地变量改变之前(3), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
        });

        executorService.execute(() -> {
            sleep(1L);
            System.out.println(String.format("本地变量改变之前(3), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
        });

        sleep(1L); //确保上面的会在tl.set执行之前执行
        ttl.set(4); // 等上面的线程池第一次启用完了,父线程再给自己赋值

        executorService.execute(() -> {
            sleep(1L);
            System.out.println(String.format("本地变量改变之后(4), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
        });

        executorService.execute(() -> {
            sleep(1L);
            System.out.println(String.format("本地变量改变之后(4), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
        });

        executorService.execute(() -> {
            sleep(1L);
            System.out.println(String.format("本地变量改变之后(4), 父线程名称-%s, 子线程名称-%s, 变量值=%s", mainThreadName, Thread.currentThread().getName(), ttl.get()));
        });

        System.out.println(String.format("线程名称-%s, 变量值=%s", Thread.currentThread().getName(), ttl.get()));

    }).start();

}

private static void sleep(long longTime) {
    try {
        Thread.sleep(longTime);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

}


最终结果如下,线程池中子线程拿到的是父线程中设置的值。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/84f356ed125c4b5da3fd29f4e7359507.png)


## 总结




![img](https://img-blog.csdnimg.cn/img_convert/a0a75e1535dc1cc05c9752f5cb5c1fc8.png)
![img](https://img-blog.csdnimg.cn/img_convert/498cb27f10c0eeed0a7387d8cd209103.png)
![img](https://img-blog.csdnimg.cn/img_convert/4b6e0a6a5812f866e1b6fe12af6869d2.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**

da3fd29f4e7359507.png)


## 总结




[外链图片转存中...(img-7UiyaDEU-1726103571859)]
[外链图片转存中...(img-HLJinvhx-1726103571860)]
[外链图片转存中...(img-kjR5jeSl-1726103571860)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!**

Spring Boot中处理循环依赖通常通过以下几个步骤: 1. **控制反转**(Inversion of Control, IoC)容器:Spring Boot的核心就是Spring框架,它通过IoC容器来管理bean之间的依赖关系。如果存在循环依赖,Spring会抛出`CircularDependencyError`异常。这时,你应该调整你的设计,避免直接将某个bean设置为另一个bean的属性,而是让它们通过方法注入或者事件驱动的方式来交互。 2. **懒加载(Lazy Initialization)**:对于那些不是立刻就需要初始化的循环依赖,可以使用`@Lazy`注解来延迟其初始化。这样可以在真正使用到该bean的时候才去创建,从而避免循环依赖。 3. **拆分模块**:如果循环依赖是因为业务逻辑过于复杂而造成的,那么可能需要对服务进行拆分,将相关的功能分离成独立的服务或者组件,降低它们之间的耦合度。 关于ThreadLocal传递参数ThreadLocal是一种线程本地变量,每个线程都有一个该变量的副本,互不影响。你可以这样做: - **设置ThreadLocal**:在需要的地方,你可以使用`ThreadLocal.set()`方法来设置值,例如在方法开始前: ```java ThreadLocal<MyData> threadLocal = new ThreadLocal<>(); threadLocal.set(myDataObject); ``` - **获取ThreadLocal**:在需要访问这个值的地方,比如在方法内: ```java MyData myData = threadLocal.get(); ``` - **清理ThreadLocal**:如果你希望在某时刻结束时移除ThreadLocal,可以使用`ThreadLocal.remove()`,或者在方法结束时自动清除: ```java try (AutoCloseable ignored = () -> threadLocal.remove()) { // ... } ``` 需要注意的是,ThreadLocal并不适合用来频繁地传递大量数据,因为它每次创建都会占用额外的空间,而且不适合跨线程共享状态。对于这类情况,一般推荐使用Spring的`ThreadLocalTemplate`或者传递对象本身作为参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值