个人理解:

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

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

  2、由1中知道,一个ThreadLocal只能存放一个线程的本地变量,所以要存放第二个线程的本地变量,就应该在new ThreadLocal进行存储,所以  private static final ThreadLocal <T> 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<ThreadLocal,value>用来保存变量的副本,只供线程自己使用。

    (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、博客园-海子-http://www.cnblogs.com/dolphin0520/p/3920407.html

  2、http://www.iteye.com/topic/103804

  3、http://www.cnblogs.com/xzwblog/p/7227509.html

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

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<Long> longLocal = new ThreadLocal<Long>();
ThreadLocal<String> stringLocal = new ThreadLocal<String>(); 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());
}
}

  

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

 

详情移步:博客园-海子-http://www.cnblogs.com/dolphin0520/p/3920407.html

 
 
 

Java并发机制(4)--ThreadLocal线程本地变量(转)的更多相关文章

  1. Threadlocal线程本地变量理解

    转载:https://www.cnblogs.com/chengxiao/p/6152824.html 总结: 作用:ThreadLocal 线程本地变量,可用于分布式项目的日志追踪 用法:在切面中生 ...

  2. ThreadLocal 线程本地变量 及 源码分析

    ■ ThreadLocal 定义 ThreadLocal通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量 ...

  3. ThreadLocal线程本地变量

    首先说明ThreadLocal存放的值是线程内共享的,线程间互斥的,主要用于线程内共享一些数据,避免通过参数来传递,这样处理后,能够优雅的解决一些实际问题,比如hibernate中的OpenSessi ...

  4. 深入理解线程本地变量ThreadLocal

    ThreadLocal理解: 假设在多线程并发环境中.一个可变对象涉及到共享与竞争,那么该可变对象就一定会涉及到线程间同步操作,这是多线程并发问题. 否则该可变对象将作为线程私有对象,可通过Threa ...

  5. Java并发(二十):线程本地变量ThreadLocal

    ThreadLocal是一个本地线程副本变量工具类. 主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不同的 ...

  6. 深入理解java:2.4. 线程本地变量 java.lang.ThreadLocal类

    ThreadLocal,很多人都叫它做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多. 可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本,那样每个线程可以访问自己内 ...

  7. 线程本地变量ThreadLocal

    一.本地线程变量使用场景 并发应用的一个关键地方就是共享数据.如果你创建一个类对象,实现Runnable接口,然后多个Thread对象使用同样的Runnable对象,全部的线程都共享同样的属性.这意味 ...

  8. java线程本地变量

      ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量).也许把它命名为Thre ...

  9. 线程本地变量ThreadLocal源码解读

      一.ThreadLocal基础知识 原始线程现状: 按照传统经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用synchronized进行线程同步.但是Spring中的各种模板 ...

随机推荐

  1. Solon 1.6.25 发布,轻量级应用开发框架

    关于官网 千呼万唤始出来: https://solon.noear.org .整了一个月多了...还得不断接着整! 关于 Solon Solon 是一个轻量级应用开发框架.支持 Web.Data.Jo ...

  2. Spring Cloud 中自定义外部化扩展机制原理及实战

    Spring Cloud针对Environment的属性源功能做了增强, 在spring-cloud-contenxt这个包中,提供了PropertySourceLocator接口,用来实现属性文件加 ...

  3. 已经安装的nginx增加额外配置步骤

    这里以安装第三方ngx_http_google_filter_module模块为例nginx的模块是需要重新编译nginx,而不是像apache一样配置文件引用.so1. 下载第三方扩展模块ngx_h ...

  4. IC应届生40万白菜价!从业多年的资深专家手把手指导你如何选择offer!

    这是IC男奋斗史的第1篇原创 关注公众号[IC男奋斗史],让我们一起撸起袖子加油干! 芯片行业2021年的秋招市场又是风起云涌.高手过招,继OPPO给应届生开出40万白菜价offer之后,平头哥又被爆 ...

  5. C#控制台输出瑞典、中文显、日语显示乱码问题

    问题:瑞典文字的"æ"在控制台中无法正确的显示? String[] strings1 = { "case", "encyclopædia", ...

  6. 关于Cookie的一些小饼干

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOEx ...

  7. Python:格式化字符串的几种方式

    1.% 'abc%s'%'123' 'abc123' 'abc%s%s'%('123','456') 'abc123456' 当变量v是一个Tuple.List且其中元素数量和字符串中%数量相同时,可 ...

  8. iOS实现组件录屏视频不可见,用户肉眼可见(类似系统键盘效果)

    系统键盘在密码框输入时,如果用户开启录屏,键盘在录屏得到的视频里会不可见,但是用户在录屏时却能看到. 为了实现这个效果,利用UItextfield在录屏下视频不可见的特性,将实现这一效果的私有UIvi ...

  9. Chapter06 数组(Array)

    目录 Chapter06 数组 6.1 数组的认识 6.2 数组的使用 使用方式1 - 动态初始化 使用方式2 - 动态初始化 使用方法3 - 静态初始化 6.3 数组使用的注意事项和细节 6.4 数 ...

  10. JZ-060-把二叉树打印成多行

    把二叉树打印成多行 题目描述 从上到下按层打印二叉树,同一层结点从左至右输出.每一层输出一行. 题目链接: 把二叉树打印成多行 代码 import java.util.ArrayList; impor ...