ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。
 
从线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
 
通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制。
 
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。
 
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。 

首先说明ThreadLocal存放的值是线程内共享的,线程间互斥的,主要用于线程内共享一些数据,避免通过参数来传递,这样处理后,能够优雅的解决一些实际问题,比如Hibernate中的OpenSessionInView,就是使用ThreadLocal保存Session对象,还有我们经常用ThreadLocal存放Connection,代码如:

 /**
* 数据库连接管理类
* @author 爽
*
*/
public class ConnectionManager { /** 线程内共享Connection,ThreadLocal通常是全局的,支持泛型 */
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>(); public static Connection getCurrConnection() {
// 获取当前线程内共享的Connection
Connection conn = threadLocal.get();
try {
// 判断连接是否可用
if(conn == null || conn.isClosed()) {
// 创建新的Connection赋值给conn(略)
// 保存Connection
threadLocal.set(conn);
}
} catch (SQLException e) {
// 异常处理
}
return conn;
} /**
* 关闭当前数据库连接
*/
public static void close() {
// 获取当前线程内共享的Connection
Connection conn = threadLocal.get();
try {
// 判断是否已经关闭
if(conn != null && !conn.isClosed()) {
// 关闭资源
conn.close();
// 移除Connection
threadLocal.remove();
conn = null;
}
} catch (SQLException e) {
// 异常处理
}
}
}

这样处理的好处:

  1. 统一管理Connection;
  2. 不需要显示传参Connection,代码更优雅;
  3. 降低耦合性。

ThreadLocal有四个方法,分别为:

initialValue

protected T initialValue()
返回此线程局部变量的当前线程的初始值。最多在每次访问线程来获得每个线程局部变量时调用此方法一次,即线程第一次使用 get() 方法访问变量的时候。如果线程先于 get 方法调用 set(T) 方法,则不会在线程中再调用 initialValue 方法。

该实现只返回 null;如果程序员希望将线程局部变量初始化为 null 以外的某个值,则必须为 ThreadLocal 创建子类,并重写此方法。通常,将使用匿名内部类。initialValue 的典型实现将调用一个适当的构造方法,并返回新构造的对象。

返回:
返回此线程局部变量的初始值

get

public T get()
返回此线程局部变量的当前线程副本中的值。如果这是线程第一次调用该方法,则创建并初始化此副本。

返回:
此线程局部变量的当前线程的值

set

public void set(T value)
将此线程局部变量的当前线程副本中的值设置为指定值。许多应用程序不需要这项功能,它们只依赖于 initialValue() 方法来设置线程局部变量的值。

参数:
value - 存储在此线程局部变量的当前线程副本中的值。

remove

public void remove()
移除此线程局部变量的值。这可能有助于减少线程局部变量的存储需求。如果再次访问此线程局部变量,那么在默认情况下它将拥有其initialValue

很多人对ThreadLocal存在一定的误解,说ThreadLocal中有一个全局的Map,set时执行map.put(Thread.currentThread(), value),get和remove时也同理,但SUN的大师们是否是如此实现的,我们只能去看源码了。

set方法:

 /**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
// 获取当前线程对象
Thread t = Thread.currentThread();
// 获取当前线程本地变量Map
ThreadLocalMap map = getMap(t);
// map不为空
if (map != null)
// 存值
map.set(this, value);
else
// 创建一个当前线程本地变量Map
createMap(t, value);
} /**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
// 获取当前线程的本地变量Map
return t.threadLocals;
}

这里注意,ThreadLocal中是有一个Map,但这个Map不是我们平时使用的Map,而是ThreadLocalMap,ThreadLocalMap是ThreadLocal的一个内部类,不对外使用的。当使用ThreadLocal存值时,首先是获取到当前线程对象,然后获取到当前线程本地变量Map,最后将当前使用的ThreadLocal和传入的值放到Map中,也就是说ThreadLocalMap中存的值是[ThreadLocal对象, 存放的值],这样做的好处是,每个线程都对应一个本地变量的Map,所以一个线程可以存在多个线程本地变量

get方法:

 /**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
// 如果值为空,则返回初始值
return setInitialValue();
}

有了之前set方法的分析,get方法也同理,需要说明的是,如果没有进行过set操作,那从ThreadLocalMap中拿到的值就是null,这时get方法会返回初始值,也就是调用initialValue()方法,ThreadLocal中这个方法默认返回null。当我们有需要第一次get时就能得到一个值时,可以继承ThreadLocal,并且覆盖initialValue()方法。

(完)

本文来自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/15732053

java线程本地变量的更多相关文章

  1. JAVA线程本地变量ThreadLocal和私有变量的区别

    ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些. 所以,在Java中编写线程局部变量的代码相对来说要笨 ...

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

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

  3. Java并发机制(4)--ThreadLocal线程本地变量(转)

    个人理解: 说明:看了博客园中大神写的ThreadLocal的详解,感觉还是有些迷糊,下面用自己的理解简单描述下ThreadLocal的机制(难免有误): 1.首先ThreadLocal用于存储对应线 ...

  4. linux TLS 线程本地变量

    最近在写底层hook的时候, 涉及到线程安全问题, 最开始我设计的时候使用的互斥量, 但是考虑到都是底层函数,加锁会导致性能问题, 一直在思考优化方案, 后来偶然想到,java里面有线程本地变量的AP ...

  5. 线程本地变量ThreadLocal (耗时工具)

    线程本地变量类 package king; import java.util.ArrayList; import java.util.List; import java.util.Map; impor ...

  6. 线程本地变量ThreadLocal

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

  7. 线程本地变量ThreadLocal (耗时工具)【原】

    线程本地变量类 package king; import java.util.ArrayList; import java.util.List; import java.util.Map; impor ...

  8. Threadlocal线程本地变量理解

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

  9. 通过transmittable-thread-local源码理解线程池线程本地变量传递的原理

    前提 最近一两个月花了很大的功夫做UCloud服务和中间件迁移到阿里云的工作,没什么空闲时间撸文.想起很早之前写过ThreadLocal的源码分析相关文章,里面提到了ThreadLocal存在一个不能 ...

随机推荐

  1. Android Framebuffer介绍及使用【转】

    转自:https://www.jianshu.com/p/df1213e5a0ed 来自: Android技术特工队 作者: Aaron 主页: http://www.wxtlife.com/ 原文连 ...

  2. Linux嵌入式交叉编译环境 的搭建【转】

    转自:http://blog.csdn.net/woaixiaozhe/article/details/7395435 1. 安装标准的C开发环境,由于Linux安装默认是不安装的,所以需要先安装一下 ...

  3. Django Xadmin - 重构django admin

    一.Django admin的执行流程 https://www.cnblogs.com/weihengblog/p/9122509.html 我的博客,介绍了django admin执行流程 二.Xa ...

  4. Java集合之Collection与之子类回顾

    Java学习这么久,打算这几天回顾下java的基本知识点,首先是集合. 一.常用集合类关系图 Collection |___List 有序,可重复 |___ArrayList  底层数据结构是数组,增 ...

  5. 20165203 学习基础和C语言基础调查

    一.技能学习经验及体会 对于课外技能来说,我对很多领域都略知一二,但涉足不深,例如体育领域.摄影领域.绘画领域.书法领域等等,我所能拿得出手的就是体育领域的乒乓球了.娄老师的作业题目让我的思绪又回到了 ...

  6. CI中如何保护RESTful API

    步骤5 保护RESTful API 为了保护RESTful API,可以在application/config/rest.php中设置安全保护级别,如下所示: $config['rest_auth'] ...

  7. hdu 3537 翻硬币 每次能翻1个 或2个 或3个

    N 枚硬币排成一排,有的正面朝上,有的反面朝上.我们从左开始对硬币按1 到N 编号. 第一,游戏者根据某些约束翻硬币,但他所翻动的硬币中,最右边那个硬币的必须是从正面翻到反面. 第二,谁不能翻谁输. ...

  8. CCF CSP 201709-1 打酱油

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201709-1 打酱油 问题描述 小明带着N元钱去买酱油.酱油10块钱一瓶,商家进行促销,每买 ...

  9. jquery图片延迟加载方案解决图片太多加载缓慢问题

    当在做一个图片展示站的时候,一个页面加载的图片过多会,如果服务器的带宽跟不上,明显会感觉到页面很卡,严重的浏览器也会崩溃,所以我推荐采用即看即所得的模式,当滚动到下一屏时才进行加载图片. 注意:即便如 ...

  10. 2017-2018 ACM-ICPC Pacific Northwest Regional Contest (Div. 1) M - Unsatisfying 2-Sat

    题目大意:给你 m 个式子, 问你最少再添加多少式子能使没有任何一种赋值方式使全部的式子为真. 并且在你给的式子中不能有非. 思路:根据题目给的m个式子可以建出2-Sat的图, 现在问你最少加多少个边 ...