ThreadLocal原理详解

一.对ThreadLocal的理解

ThreadLocal为变量在每个线程中都创建了一个副本,一个ThreadLocal在一个线程中是共享的,在不同线程之间又是隔离的,也就说说每个线程都只能看到自己线程的值。

二.ThreadLocal的核心作用

实现线程范围的局部变量

三.ThreadLocla的API介绍

initialValue():返回此线程局部变量的当前线程的初始值,默认为null

get():返回当前线程的此线程局部变量的副本中的值

set(T value):将当前线程的此线程局部变量的副本设器为指定的值。

remove():删除此线程局部变量的当前线程的值。

withInitial(Supplier<? extends S> supplier):创建线程局部变量。

四.ThreadLocal应用

package com.tangbb.test1;

/**
 * @DESCRIPTION:
 * @USER: tangbingbing
 * @DATE: 2023/3/13 9:49
 */
public class test1 {
    private static ThreadLocal<Integer> num = new ThreadLocal<Integer>() {
        // 重写这个方法,可以修改“线程变量”的初始值,默认是null
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

    public static void main(String[] args) {
        // 创建一号线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 在一号线程中将ThreadLocal变量设置为1
                num.set(1);
                System.out.println("一号线程中ThreadLocal变量中保存的值为:" + num.get());
            }
        }).start();

        // 创建二号线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                num.set(2);
                System.out.println("二号线程中ThreadLocal变量中保存的值为:" + num.get());
            }
        }).start();

        //为了让一二号线程执行完毕,让主线程睡500ms
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("主线程中ThreadLocal变量中保存的值:" + num.get());
    }
}

在类中创建一个静态的ThreadLocal变量,在主线程中创建两个线程,在这两个线程中分别设置ThreadLocal变量为1和2。然后等待一号和二号线程执行完毕后,在主线程中查看ThreadLocal变量的值

五.原理分析

每个Thread对象都有一个ThreadLocalMap,当创建一个ThreadLocal的时候,就会将该ThreadLocal对象添加到该Map中,其中键就是ThreadLocal,值可以是任意类型。

 

ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

六.回收举例(解决OOM问题)

为了防止OOM问题ThreadLocal在使用的时候要及时remove

package com.tangbb.test1;



/**
 * @DESCRIPTION:
 * @USER: tangbingbing
 * @DATE: 2023/3/13 9:49
 */
public class test1 {
    private static ThreadLocal<String> localVar = new ThreadLocal<String>();

    static void print(String str) {
        //打印当前线程中本地内存中本地变量的值
        System.out.println(str + " :" + localVar.get());
        //清除本地内存中的本地变量
        localVar.remove();
    }
    public static void main(String[] args) throws InterruptedException {

        new Thread(new Runnable() {
            public void run() {
                test1.localVar.set("local_A");
                print("A");
                //打印本地变量
                System.out.println("after remove : " + localVar.get());

            }
        },"A").start();

        Thread.sleep(1000);

        new Thread(new Runnable() {
            public void run() {
                test1.localVar.set("local_B");
                print("B");
                System.out.println("after remove : " + localVar.get());

            }
        },"B").start();
    }

}

七.ThreadLocal常见的使用场景

ThreadLocal可以解决具有以下两个需求的场景:

1.每个线程需要有自己单独的实例

2.实例需要在多个方法中共享,但不希望被多线程共享

场景1

数据跨层传递(controller,service, dao)

比如说我们是一个用户系统,那么当一个请求进来的时候,一个线程会负责执行这个请求,然后这个请求就会依次调用service-1()、service-2()、service-3()、service-4(),这4个方法可能是分布在不同的类中的。

package com.kong.threadlocal;
 
 
public class ThreadLocalDemo05 {
    public static void main(String[] args) {
        User user = new User("jack");
        new Service1().service1(user);
    }
 
}
 
class Service1 {
    public void service1(User user){
        //给ThreadLocal赋值,后续的服务直接通过ThreadLocal获取就行了。
        UserContextHolder.holder.set(user);
        new Service2().service2();
    }
}
 
class Service2 {
    public void service2(){
        User user = UserContextHolder.holder.get();
        System.out.println("service2拿到的用户:"+user.name);
        new Service3().service3();
    }
}
 
class Service3 {
    public void service3(){
        User user = UserContextHolder.holder.get();
        System.out.println("service3拿到的用户:"+user.name);
        //在整个流程执行完毕后,一定要执行remove
        UserContextHolder.holder.remove();
    }
}
 
class UserContextHolder {
    //创建ThreadLocal保存User对象
    public static ThreadLocal<User> holder = new ThreadLocal<>();
}
 
class User {
    String name;
    public User(String name){
        this.name = name;
    }
}
 
执行的结果:
 
service2拿到的用户:jack
service3拿到的用户:jack

场景2

数据库连接,处理数据库事务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr Tang

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值