threadLocal

本文介绍了Java中的ThreadLocal类,详细说明了如何使用initValue(), set(), get()方法,以及两种常见的使用方法。文章还探讨了ThreadLocal的工作原理,强调了ThreadLocalMap在其中的作用,并提醒在Web项目中使用ThreadLocal需要注意及时清理以防止内存泄漏。最后,文章提出了关于ThreadLocal在不同场景下行为的疑问。" 118059293,8753399,Boost库unwrap_cv_reference测试,"['C++编程', 'Boost库', '类型转换', '函数参数']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                                                                       怎么用:


         我们使用ThreadLocal主要通过三个方法, initValue(), set(),get() ;

         initValue : 设置初始值(ThrealLocal 在初始化的时候会自动调用这个方法来设置初始值);

         set:            设置值,重新赋值;

         get :           获取值;

         使用的时候一般有两种使用方法:

        方法1:  在new的时候重写initVlue方法,设置初始值,然后通过get方法获取值,set方法重新赋值;

        方法2:  在当前类的构造函数中通过set方法,设置初始值,然后通过get方法获取值,set方法重新赋值;

                                                          

                                                    方法1代码:(测试类,demo,控制台打印结果)

测试类:        

public class ThreadLocalTest implements Runnable{

    //重写initValue方法初始化变量
    private ThreadLocal<Integer> i = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };


    //run方法
public void run() {
    this.run1();
}

private void run1(){
    for (int j = 0; j < 100; j++) {
        //使用get方法获取值
        System.out.println( Thread.currentThread().getName() + "  :  "+ + j + "  :  " + i.get()  + "  :  " +( j == i.get()));

        //调用set方法重新赋值
        i.set( i.get() + 1);

        try {
            Thread.currentThread().sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("睡眠问题");
        }
    }
}
}
demo:
public class ThreadLocalDemo {

    public static void main(String[] args) {
        ThreadLocalTest test = new ThreadLocalTest();

        for (int i = 0; i < 4 ; i++) {
     
  //注意这里创建线程时传进去的runable是同一个ThreadLocalTest实例,也就是每个线程使用了该实例同一个成员变量,并在run方法中对该成员进行了并发操作.后面的结果我们会看到虽然进行了并发操作,但是数据却是安全的

      Thread thread = newThread(test);
            thread.setName("thread " + i);
            thread.start();
        }
    }
}

结果:全部为true,也就是说并发情况下ThreadLocal确保了数据的安全性。

(通过打印语句中的 j == i.get() 来确定线程是否安全,结果全部为true)

thread 0  :  0  :  0  :  true
thread 1  :  0  :  0  :  true
thread 2  :  0  :  0  :  true
thread 3  :  0  :  0  :  true
thread 0  :  1  :  1  :  true
thread 1  :  1  :  1  :  true
thread 3  :  1  :  1  :  true
thread 2  :  1  :  1  :  true
thread 0  :  2  :  2  :  true

。。。

thread 3  :  98  :  98  :  true
thread 1  :  99  :  99  :  true
thread 2  :  98  :  98  :  true
thread 0  :  99  :  99  :  true
thread 3  :  99  :  99  :  true

thread 2  :  99  :  99  :  true


                                                                  方法2代码:(随后上传)


                                                                           原理(浅层):

关键的类是: ThreadLocalMap  (以ThreadLocal 为key , object为value ) 

这个类是ThreadLocal 的一个内部类 (是它的内部类,但却不是它的成员变量,只是局部变量;设计成内部类只因为该类无需被大范围使用。所以内部类这点不用关注,不是重点,设计成外部的类也完全可以,完全忽视它内部类的身份就可以了)。 

同时也是Thread类中的一个成员变量(ThreadLocalMap threadLocals)(这个才是重点) 。

当往一个ThreadLocal对象中存值时,实际上是获取到当前线程,然后往当前线程的成员变量ThreadLocalMap threadLocals 中存值,key 为threadLocal, value 为要存的值。取值时也是获取当前线程,然后从其成员变量ThreadLocalMap threadLocals中取值。所以我们存的值是和当前线程绑定到了一起。


需要注意的是ThreadLocalMap和其他map一样是,可以存放多组 key value的,所以一个线程在执行过程中是一般是存放了多组 key value 的。

Thread类并没有对外提供ThreadLocalMap threadLocals的访问方式,所以我们并不能直接访问这个map。

ThreadLocal之所以能访问ThreadLocalMap threadLocals是因为它和Thread在同一个包下,并且ThreadLocalMap threadLocals的权限设置的是default。

ThreadLocalMap这个类是深入了解ThreadLocal的关键,深入研究就去研究ThreadLocalMap这个类,比如它的继承关系,引用强弱。它的有一个内部类,这个内部类继承了WeakReference,研究边知道ThreadLocalMap和其他的map不同,它根本没有实现map接口。

          

                                                                           注意事项:

1:   如果是web项目,一定要注意在使用完ThrealLocal后及时的调用remove 方法及时销毁储存的对象;

        因为对象和当前线程进行了绑定,而当前线程使用完之后可能不会被销毁而是被放到了线程池中,如果久而久之会造成内存溢出的.(这点是存在争议的要去研究一下)

        fengxiu的动态datasource 似乎不用关心这个问题;

2: 如果是重写initValue 方法来进行赋值,要注意不能注入引用对象,多个对象将会引用这同一个对象,导致线程不安全;

3 : 如果是set方法来进行赋值,要注意不能注入引用对象,一方面是线程不安全,一方面是set后,再get可能get不到。


                                                                           不清楚的问题:

1:   

         如果在一个方法中set了一个map对象,然后在另外一个方法中get,结果get到的是有时候是null(甚至全部为null)?

         如果不是set方式初始化,而是init方法初始化map对象,那么就不会出现上面的问题?

         如果init方法进行了初始化map,后面再交替使用get和set,那么会出现有时候get到initValue,有时候get到上次的set值?(这个问题的答案比较简单,get时先去获取上次set的值,如果get到的为null,再去获取initValue,所以get到的是set和initaValue交替)

        参考连接:http://blog.51cto.com/zhangjunhd/53092

                         https://blog.youkuaiyun.com/wtjrenranwtj/article/details/50527565

2     一个子线程是如何获取到父线程的ThreadLocal变量?

    是通过 Thread 中 inheritable 这个成员变量吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值