线程本地变量ThreadLocal源码解读

 

一、ThreadLocal基础知识

 

原始线程现状: 按照传统经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用synchronized进行线程同步。但是Spring中的各种模板类并未采用线程同步机制,因为线程同步会影响并发性和系统性能,而且实现难度也不小。

ThreadLocal在Spring中发挥着重要的作用。在管理request作用域的bean,事务管理,任务调度,AOP等模块中都出现了它的身影。

ThreadLocal介绍: 它不是一个线程,而是线程的一个本地化对象,或者说是局部变量。当工作于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本。所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本,将类变量放到ThreadLocal类型的对象中,使变量在每个线程中都有独立拷贝,不会出现一个线程读取变量时而被另一个线程修改的现象

如何存放线程本地变量?

在ThreadLocal类中有一个ThreadLocalMap, 用于存放每一个线程的变量副本,Map中元素的键为线程对象,值为对应线程的变量副本。

二、源码解读

 
 

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

1/**
2 * 数据库连接管理类
3 * @author 爽
4 *
5 */
6public class ConnectionManager {
7
8    /** 线程内共享Connection,ThreadLocal通常是全局的,支持泛型 */
9    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
10
11    public static Connection getCurrConnection() {
12        // 获取当前线程内共享的Connection
13        Connection conn = threadLocal.get();
14        try {
15            // 判断连接是否可用
16            if(conn == null || conn.isClosed()) {
17                // 创建新的Connection赋值给conn(略)
18                // 保存Connection
19                threadLocal.set(conn);
20            }
21        } catch (SQLException e) {
22            // 异常处理
23        }
24        return conn;
25    }
26
27    /**
28     * 关闭当前数据库连接
29     */
30    public static void close() {
31        // 获取当前线程内共享的Connection
32        Connection conn = threadLocal.get();
33        try {
34            // 判断是否已经关闭
35            if(conn != null && !conn.isClosed()) {
36                // 关闭资源
37                conn.close();
38                // 移除Connection
39                threadLocal.remove();
40                conn = null;
41            }
42        } catch (SQLException e) {
43            // 异常处理
44        }
45    }
} 

这样处理的好处:

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()方法

优秀博文:

ThreadLocal

时间: 2024-10-07 22:17:27

线程本地变量ThreadLocal源码解读的相关文章

深入理解线程本地变量ThreadLocal

ThreadLocal理解: 如果在多线程并发环境中,一个可变对象涉及到共享与竞争,那么该可变对象就一定会涉及到线程间同步操作,这是多线程并发问题. 否则该可变对象将作为线程私有对象,可通过ThreadLocal进行管理,实现线程间私有对象隔离的目的. 可以发现,ThreadLocal并没有解决多线程并发的问题,因为ThreadLocal管理的可变对象的性质本来就不会涉及到多线程并发而引发的共享.竞争和同步问题,使用ThreadLocal管理只是方便了多线程获取和使用该私有可变对象的途径和方式.

线程本地变量ThreadLocal

一.本地线程变量使用场景 并发应用的一个关键地方就是共享数据.如果你创建一个类对象,实现Runnable接口,然后多个Thread对象使用同样的Runnable对象,全部的线程都共享同样的属性.这意味着,如果你在一个线程里改变一个属性,全部的线程都会受到这个改变的影响. 有时,你希望程序里的各个线程的属性不会被共享. Java 并发 API提供了一个很清楚的机制叫本地线程变量即ThreadLocal. 模拟ThreadLocal类实现:线程范围内的共享变量,每个线程只能访问他自己的,不能访问别的

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

ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些. 所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及. ThreadLocal的接口方法 ThreadLocal类接口很简单,只有4个方法,我们先来了解一下: void set(Object value) public void remove() 将当前线程局部变量的值删除,目的是为了减

java ThreadLocal线程设置私有变量底层源码分析

前面也听说了ThreadLocal来实现高并发,以前都是用锁来实现,看了挺多资料的,发现其实还是区别挺大的(感觉严格来说ThreadLocal并不算高并发的解决方案),现在总结一下吧. 高并发中会出现的问题就是线程安全问题,可以说是多个线程对共享资源访问如何处理的问题,处理不当会的话,会出现结果和预期会完全不同. 一般情况下,多个线程访问一个变量都是公用他们的值,不过有时候虽然也是访问共享变量,不过每个线程却需要自己的私有变量.这个时候ThreadLocal就有用武之地了.下面是个ThreadL

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

转自:博客园-海子-http://www.cnblogs.com/dolphin0520/p/3920407.html Java并发编程:深入剖析ThreadLocal 首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的.各个线程中访问的是不同的对象. 另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal

struct2源码解读(9)之处理Action请求

struct2源码解读之处理Action请求 我们前面讨论过了struct2的初始化,我们先来回顾下  public void init(FilterConfig filterConfig) throws ServletException {         InitOperations init = new InitOperations();         try {             FilterHostConfig config = new FilterHostConfig(fil

java线程本地变量

ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量).也许把它命名为ThreadLocalVar更加合适.线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突. 从线程的角度看,每个线程都保持一个对其线程局部变量副本

ThreadLocal源码调试——“this”作为key

前言:在一次面试过程中被问到ThreadLocal,大家都知道ThreadLocal可以为每个线程单独提供一个副本,从而实现变量间的隔离.但是ThreadLocal中的key是什么,ThreadLocal又是怎样各线程间互不干扰的呢,下面通过对ThreadLocal源码的调试来具体理清这些问题. 1.set与get源码 1 public void set(T value) { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map =

【JAVA】ThreadLocal源码分析

ThreadLocal内部是用一张哈希表来存储: 1 static class ThreadLocalMap { 2 static class Entry extends WeakReference<ThreadLocal<?>> { 3 /** The value associated with this ThreadLocal. */ 4 Object value; 5 6 Entry(ThreadLocal<?> k, Object v) { 7 super(k)