java线程本地变量_Java并发机制(4)--ThreadLocal线程本地变量(转)

个人理解:

说明:看了博客园中大神写的ThreadLocal的详解,感觉还是有些迷糊,下面用自己的理解简单描述下ThreadLocal的机制(难免有误);

1、首先ThreadLocal用于存储对应线程的本地变量,放在哪里呢?每一个线程维护一个threadlocals(这个threadlocals我认为是由ThreadLcoal创建的,是当前线程上的属性,多个ThreadLocal只创建一个:通过ThreadLocal中的createMap方法: t.threadLocals = new ThreadLocalMap(this, firstValue);),它事实上就是一个ThreadLocalMap,是ThreadLocal的内部类,可以理解为一张Map表,其中包含了Entry键值对,故线程的本地变量就放在这个Entry中:Entry:形式,键值对里的ThreadLocal我认为是你用的时候new出来的ThreadLocal,而不是每个线程所维护的那一个ThreadLoca。(不是很确定,但是想到一个线程要放多个变量的话,每个变量new一个ThreadLocal,这些ThreadLocal做为key放在线程所维护的那个ThreadLocalMap中?)。

2、由1中知道,一个ThreadLocal只能存放一个线程的本地变量,所以要存放第二个线程的本地变量,就应该在new ThreadLocal进行存储,所以  private static final ThreadLocal t1=new Threadlocal<>() 这句话可以写在工具类中,且作为静态成员变量使用。

这里我一开始理解错误:把ThreadLocalMap理解成类似于HashMaple了,然后ThreadLocal保存了所有线程的本地变量,这样一个ThreadLocal就可以了?那么多线程访问一个ThreadLocal(共享)依然需要加锁,失去了原来的意义。

3、ThreadLocal的方法理解(源码分析):

3.1、threadlocal.get()方法:

step1:根据当前线程t获取对应的TheradLocalMap;

step2:如果ThreadLocalMap!=null;获取内部的Entry,返回val,结束;

如果ThreadLocalMap==null,调用setInitialValue()方法设置初始值;

step3:setInitialValue()方法判断是否有ThreadLocalMap,(没有则创建),

并调用initialValue()方法获得初始value,这个value=null;

所以,new 出来的Threadlocal要先set(value),然后get(),否则get到null;

或者也可以重写initialValue方法(默认返回一个初始值);

3.2、threadlocal.set()方法:赋值,不多描述。

4、弱引用问题:ThreadLocalMap使用ThreadLocal的弱引用作为key,,如果一个ThreadLocal没有外部强引用引用他,那么系统gc的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,无法访问到这些value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value,永远无法回收,造成内存泄露。

JDK建议将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,并主动调用remove函数释放。

总结如下:

首先,要区分threadlocals、ThreadLocalMap、ThreadLocal这三者。

其次,ThreadLcoal的主要机制如下。

(1)每个线程Thread内部有一个threadlocals属性,它是ThreadLocal.ThreadLocalMap类型的,内部的Entry用来保存变量的副本,只供线程自己使用。

(2)最开始,threadlocals是空的,通过Threadlocal的get或者set方法可以初始化threadlocals(调用createMap方法,源代码在1种),然后你每存一个变量副本,需要new ThreadLocal()出来,存几个new几个,threadlocals以该ThreadLocal为key,变量副本为value进行保存。

(2)我们可以通过ThreadLocal.get()获取对应的ThreadLocal的value。通过ThreadLcoal.set(value)来设置更改threadlocals中的值。

参考资料:

以下给出两段实例代码,也来自上述参考资料:

1、hibernate中典型的ThreadLocal的应用,获取session;

...

private static final ThreadLocal threadSession = new ThreadLocal(); //类成员

public static Session getSession() throws InfrastructureException {

Session s = (Session) threadSession.get();

try {

if (s == null) {

s = getSessionFactory().openSession();

threadSession.set(s);

}

} catch (HibernateException ex) {

throw new InfrastructureException(ex);

}

return s;

}

2、

public class Test {

ThreadLocal longLocal = new ThreadLocal();

ThreadLocal stringLocal = new ThreadLocal();

public void set() {

longLocal.set(Thread.currentThread().getId());

//这里先获取每一个线程thread的ThreadLocalMap,然后map.set(this, value),this就是当前的ThreadLocal,value就是getId的值,也就为每个线程创建完副本,并存放入线程的ThreadLocalMap中,从而实现了线程本地变量的副本创建。

stringLocal.set(Thread.currentThread().getName());

}

public long getLong() {

return longLocal.get();

}

public String getString() {

return stringLocal.get();

}

public static void main(String[] args) throws InterruptedException {

final Test test = new Test();

test.set();

System.out.println(test.getLong());

System.out.println(test.getString());

Thread thread1 = new Thread(){

public void run() {

test.set();

System.out.println(test.getLong());

System.out.println(test.getString());

};

};

thread1.start();

thread1.join();

System.out.println(test.getLong());

System.out.println(test.getString());

}

}

2eefd1ba07a8487def6acdb706f09c07.png

------------------------------------------------------------------------------------------终-----------------------------------------------------------------------------------------------------------------------------------------------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值