ThreadLocal详解

一.对ThreadLocal的理解

二.深入解析ThreadLocal类

三.ThreadLocal的应用场景

参考:
注意原链接的评论部分,作者有错误的地方
Java并发编程:深入剖析ThreadLocal

对ThreadLocal的理解

ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本(实际上是将变量保存在Map),那么每个线程可以访问自己内部的副本变量。
  这句话从字面上看起来很容易理解,但是真正理解并不是那么容易。
  我们还是先来看一个例子:

Paste_Image.png

假设有这样一个数据库链接管理类,这段代码在单线程中使用是没有任何问题的,但是如果在多线程中使用呢?很显然,在多线程中使用会存在线程安全问题:第一,这里面的2个方法都没有进行同步,很可能在openConnection方法中会多次创建connect;第二,由于connect是共享变量,那么必然在调用connect的地方需要使用到同步来保障线程安全,因为很可能一个线程在使用connect进行数据库操作,而另外一个线程调用closeConnection关闭链接。

所以出于线程安全的考虑,必须将这段代码的两个方法进行同步处理,并且在调用connect的地方需要进行同步处理。

这样将会大大影响程序执行效率,因为一个线程在使用connect进行数据库操作的时候,其他线程只有等待。

那么大家来仔细分析一下这个问题,这地方到底需不需要将connect变量进行共享?事实上,是不需要的。假如每个线程中都有一个connect变量,各个线程之间对connect变量的访问实际上是没有依赖关系的,即一个线程不需要关心其他线程是否对这个connect进行了修改的。

深入解析ThreadLocal类

先了解一下ThreadLocal类提供的几个方法:

Paste_Image.png

get()方法是用来获取ThreadLocal在当前线程中保存的变量副本,set()用来设置当前线程中变量的副本,remove()用来移除当前线程中变量的副本,initialValue()是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法,下面会详细说明。

首先我们来看一下ThreadLocal类是如何为每个线程创建一个变量的副本的。

public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
第一句是取得当前线程,然后通过getMap(t)方法获取到一个map,map的类型为ThreadLocalMap。然后接着下面获取到<key,value>键值对,注意这里获取键值对传进去的是 this,而不是当前线程t。
  如果获取成功,则返回value值。
  如果map为空,则调用setInitialValue方法返回value。
  我们上面的每一句来仔细分析:
  首先看一下getMap方法中做了什么:
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
可能大家没有想到的是,在getMap中,是调用当期线程t,返回当前线程t中的一个成员变量threadLocals。
  那么我们继续取Thread类中取看一下成员变量threadLocals是什么:
  


  实际上就是一个ThreadLocalMap,这个类型是ThreadLocal类的一个内部类,我们继续取看ThreadLocalMap的实现:
  

  可以看到ThreadLocalMap的Entry继承了WeakReference,并且使用ThreadLocal作为键值。
然后再继续看setInitialValue方法的具体实现:

  很容易了解,就是如果map不为空,就设置键值对,为空,再创建Map,看一下createMap的实现:
  

  至此,可能大部分朋友已经明白了ThreadLocal是如何为每个线程创建变量的副本的:
  首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。
  初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
  然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。

ThreadLocal的应用场景

最常见的ThreadLocal使用场景为 用来解决 数据库连接、Session管理等。

Paste_Image.png

下面这段代码摘自:
  http://www.iteye.com/topic/103804

Paste_Image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • ThreadLocal在java.lang包中,其主要作用是提供一个和线程绑定的变量环境,即通过ThreadLoc...
    charming_coder阅读 1,336评论 1 8
  • 1. 概念 ThreadLocal 用于提供线程局部变量,在多线程环境可以保证各个线程里的变量独立于其它线程里的变...
    zly394阅读 1,832评论 0 1
  • ThreadLocal翻译成中文比较准确的叫法应该是:线程局部变量。 这个玩意有什么用处,或者说为什么要有这么一个...
    小小的梦想008阅读 155评论 0 0
  • 电视剧《我的前半生》的主人公是罗子君,过了十年的富太生活,却突遭丈夫抛弃,不得不自食其力,与生活抗争。大部分人只关...
    梧桐妈阅读 545评论 0 0
  • 抬起平静的手 止不住乱发的誓 燕过檐留不下背影 雨整夜拢不起相思 昨日又复昨日醉 明日岂可待人归? 转灯漏光的角落...
    沉默的话痨阅读 246评论 2 5