ThreadLocal为什么称作线程局部变量

一、RTFSC

java.lang.ThreadLocal<T>的具体实现

那么到底ThreadLocal类是如何实现这种“为每个线程提供不同的变量拷贝”的呢?先来看一下ThreadLocal的set()方法的源码是如何实现的:

[java] view plaincopyprint?

  1. /**
  2. * Sets the current thread‘s copy of this thread-local variable
  3. * to the specified value.  Most subclasses will have no need to
  4. * override this method, relying solely on the {@link #initialValue}
  5. * method to set the values of thread-locals.
  6. *
  7. * @param value the value to be stored in the current thread‘s copy of
  8. *        this thread-local.
  9. */
  10. public void set(T value) {
  11. Thread t = Thread.currentThread();
  12. ThreadLocalMap map = getMap(t);
  13. if (map != null)
  14. map.set(this, value);
  15. else
  16. createMap(t, value);
  17. }
<span style="font-size:14px;"> /**
     * 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();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }</span>

在这个方法内部我们看到,首先通过getMap(Thread t)方法获取一个和当前线程相关的ThreadLocalMap,然后将变量的值设置到这个ThreadLocalMap对象中,当然如果获取到的ThreadLocalMap对象为空,就通过createMap方法创建。

线程隔离的秘密,就在于ThreadLocalMap这个类。ThreadLocalMap是ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取(对比Map对象来理解),每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。ThreadLocal类通过操作每一个线程特有的ThreadLocalMap副本,从而实现了变量访问在不同线程中的隔离。因为每个线程的变量都是自己特有的,完全不会有并发错误。还有一点就是,ThreadLocalMap存储的键值对中的键是this对象指向的ThreadLocal对象,而值就是你所设置的对象了。

为了加深理解,我们接着看上面代码中出现的getMap和createMap方法的实现:

[java] view plaincopyprint?

  1. /**
  2. * Get the map associated with a ThreadLocal. Overridden in
  3. * InheritableThreadLocal.
  4. *
  5. * @param  t the current thread
  6. * @return the map
  7. */
  8. ThreadLocalMap getMap(Thread t) {
  9. return t.threadLocals;
  10. }
  11. /**
  12. * Create the map associated with a ThreadLocal. Overridden in
  13. * InheritableThreadLocal.
  14. *
  15. * @param t the current thread
  16. * @param firstValue value for the initial entry of the map
  17. * @param map the map to store.
  18. */
  19. void createMap(Thread t, T firstValue) {
  20. t.threadLocals = new ThreadLocalMap(this, firstValue);
  21. }
<span style="font-size:14px;">    /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     * @param map the map to store.
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }</span>

接下来再看一下ThreadLocal类中的get()方法:

[java] view plaincopyprint?

  1. /**
  2. * Returns the value in the current thread‘s copy of this
  3. * thread-local variable.  If the variable has no value for the
  4. * current thread, it is first initialized to the value returned
  5. * by an invocation of the {@link #initialValue} method.
  6. *
  7. * @return the current thread‘s value of this thread-local
  8. */
  9. public T get() {
  10. Thread t = Thread.currentThread();
  11. ThreadLocalMap map = getMap(t);
  12. if (map != null) {
  13. ThreadLocalMap.Entry e = map.getEntry(this);
  14. if (e != null)
  15. return (T)e.value;
  16. }
  17. return setInitialValue();
  18. }
<span style="font-size:14px;">    /**
     * 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();
    }</span>

再来看setInitialValue()方法:

[java] view plaincopyprint?

  1. /**
  2. * Variant of set() to establish initialValue. Used instead
  3. * of set() in case user has overridden the set() method.
  4. *
  5. * @return the initial value
  6. */
  7. private T setInitialValue() {
  8. T value = initialValue();
  9. Thread t = Thread.currentThread();
  10. ThreadLocalMap map = getMap(t);
  11. if (map != null)
  12. map.set(this, value);
  13. else
  14. createMap(t, value);
  15. return value;
  16. }
<span style="font-size:14px;"> /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }</span>

  获取和当前线程绑定的值时,ThreadLocalMap对象是以this指向的ThreadLocal对象为键进行查找的,这当然和前面set()方法的代码是相呼应的。

  进一步地,我们可以创建不同的ThreadLocal实例来实现多个变量在不同线程间的访问隔离,为什么可以这么做?因为不同的ThreadLocal对象作为不同键,当然也可以在线程的ThreadLocalMap对象中设置不同的值了。通过ThreadLocal对象,在多线程中共享一个值和多个值的区别,就像你在一个HashMap对象中存储一个键值对和多个键值对一样,仅此而已。

另外看一下ThreadLocalMap中的set方法

/**

* Set the value associated with key.

*

* @param key the thread local object

* @param value the value to be set

*/

private void set(ThreadLocal key, Object value) {

// We don‘t use a fast path as with get() because it is at

// least as common to use set() to create new entries as

// it is to replace existing ones, in which case, a fast

// path would fail more often than not.

Entry[] tab = table;

int len = tab.length;

int i = key.threadLocalHashCode & (len-1);

for (Entry e = tab[i];

e != null;

e = tab[i = nextIndex(i, len)]) {

ThreadLocal k = e.get();

if (k == key) {

e.value = value;

return;

}

if (k == null) {

replaceStaleEntry(key, value, i);

return;

}

}

tab[i] = new Entry(key, value);

int sz = ++size;

if (!cleanSomeSlots(i, sz) && sz >= threshold)

rehash();

}

实现的是对value的浅拷贝,使用时需要注意。只有保存进去的对象不是共享的对象才没有共享问题,换句话说存进去的都是每个线程new出来的对象就没有并发问题

二、和锁机制的区别

锁机制是采用让线程排队通过的方法,而线程局部变量时为每个线程拷贝副本的方式,总结一句话就是一个是锁机制进行时间换空间,一个是存储拷贝进行空间换时间。

如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。

三、典型应用

Hibernate的文档时看到了关于使ThreadLocal管理多线程访问的部分。具体代码如下

1. public static final ThreadLocal session = new ThreadLocal();

2. public static Session currentSession() {

3. Session s = (Session)session.get();

4. //open a new session,if this session has none

5. if(s == null){

6. s = sessionFactory.openSession();

7. session.set(s);

8. }

return s;

9. }

我们逐行分析

1。 初始化一个ThreadLocal对象,ThreadLocal有三个成员方法 get()、set()、initialvalue()。

如果不初始化initialvalue,则initialvalue返回null。

3。session的get根据当前线程返回其对应的线程内部变量,也就是我们需要的net.sf.hibernate.Session(相当于对应每个数据库连接).多线程情况下共享数据库链接是不安全的。ThreadLocal保证了每个线程都有自己的s(数据库连接)。

5。如果是该线程初次访问,自然,s(数据库连接)会是null,接着创建一个Session,具体就是行6。

6。创建一个数据库连接实例 s

7。保存该数据库连接s到ThreadLocal中。

8。如果当前线程已经访问过数据库了,则从session中get()就可以获取该线程上次获取过的连接实例。







时间: 2024-10-10 10:36:34

ThreadLocal为什么称作线程局部变量的相关文章

Java中线程局部变量ThreadLocal使用教程及源码分析

在Java多线程编程中有时候会遇见线程本地局部变量ThreadLocal这个类,下面就来讲讲ThreadLocal的使用及源码分析. ThreadLocal 是Thread Local Varial(线程局部变量)的意思,每个线程在使用线程局部变量的时候都会为使用这个线程局部变量的线程提供一个线程局部变量的副本,使得每个线程都可以完全独立地操作这个线程局部变量,而不会与其他线程发生冲突,从线程的角度来看,每个线程都好像独立地拥有了这个线程局部变量.这样,看似每个线程都在并发访问同一个资源(线程局

ThreadLocal共享线程局部变量和线程同步机制的区别

ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题.在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性. 对于多线程资源共享的问题,同步机制采用了"以时间换空间"的方式,而ThreadLocal采用了"以空间换时间"的方式.前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同

ThreadLocal线程局部变量

1.ThreadLocal说明 线程内的共享数据:方法,表达式或者是模块,当他们在同一线程上运行,他们访问同一变量,应该访问的是同一数据.将数据与线程绑定到一起.换句话说,我线程内的事在我的线程内完成,不受其他线程的影响.线程内共享同一数据对象.即在线程内共享,在线程外独立. public Map<Thread,Integer> threadData=new HashMap<Thread,Integer>();threadData.put(Thread.currentthread,

JAVA并发编程之线程局部变量

共享数据是并发程序最核心的问题之一,对于继承Thread类或者实现Runnable接口的对象来说尤其重要. 如果创建的对象实现了Runnable接口的类的实例,用它作为传入参数,并创建多个线程对象并启动这些线程,那么所有的线程将共享相同的属性.如果在一个线程中改变一个属性,所有线程都会被这个改变影响. 在某种情况下,这个对象的属性不需要被所有线程共享.JAVA提供了一个比较好的机制,即线程局部变量(Thread-Local Variable). 我们写一个简单的DEMO,一是具有刚才提到的问题,

线程局部变量的使用

共享数据是并发程序最核心的问题之一,对于继承了Thread类或者实现了Runnable接口的对象来说尤其重要.如果创建的对象是实现了Runnable接口的类的实例,用它作为传入参数创建多个线程对象并启动这些线程,那么所有的线程将共享相同的属性.也就是说,如果你在一个线程中改变了一个属性,所有线程都会被这个改变影响. 在某种情况下,这个对象的属性不需要被所有线程共享.Java并发API提供了一个干净的机制,即线程局部变量(Thread-Local Variable),其具有很好的性能. 这里我们将

[Python]threading local 线程局部变量小测试

概念 有个概念叫做线程局部变量,一般我们对多线程中的全局变量都会加锁处理,这种变量是共享变量,每个线程都可以读写变量,为了保持同步我们会做枷锁处理.但是有些变量初始化以后,我们只想让他们在每个线程中一直存在,相当于一个线程内的共享变量,线程之间又是隔离的.python threading模块中就提供了这么一个类,叫做local. 多线程中共享变量和局部变量的区别我画两个小图,简单描述下(作图能力一般,请见谅,概念性的东西大家可以google下,很多好文章) 全局变量 线程局部变量 对比: 下面是

转载 多线程开发时线程局部变量的使用

多线程开发时线程局部变量的使用 http://blog.csdn.net/zsxxsz/article/details/6284759 2011-03-28 22:37197人阅读评论(0)收藏举报 一.概述 现在多核时代多线程开发越来越重要了,多线程相比于多进程有诸多优势(当然也有诸多劣势).在早期C的库中,有许多函数是线程不安全的,因为内 部用到了静态变量,比如:char *strtok(char *s, const char *delim); 该函数内部就有一个静态指针,如果多个线程同时调

线程局部变量(ThreadLocal)

一般情况下,我们将一个线程中的局部变量保存在线程之中.本文以装Connection为例子.做法如下: 第一步: private static ThreadLocal<Connection> tl; 第二步: public static Connection getConnection() {      Connection con = tl.get();     if (con == null) {     try {         con = ds.getConnection(); tl.

手写自己的ThreadLocal(线程局部变量)

ThreadLocal对象通常用于防止对可变的单实例变量或全局变量进行共享. package com.lxc.tet; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class MyThreadLocal<T> { private Map<Thread,T> map = new ConcurrentHashMap<Thread, T>(); protected T