`

一幅图让你彻底明白ThreadLocal类型变量

 
阅读更多
看了些ThreadLocal类型变量的介绍,感觉都没有串起来说清楚。花了两小时搞清楚之后,为了其它伙伴们更容易理解ThreadLocal,咱们还是来个图文说明的方式,一图抵千言哪。如果能帮到你,还希望顶一下俺的原创。

-------
对不起啦,对java的静态内部类理解不足,下面的图对ThreadLocalMap的引用的画面可能问题,请大家指正!我会总结大家的意见修改后形成最终结论!非常感谢!
-------

针对ThreadLocal的源代码,我画了这样一张图,并把其set和get方法的伪代码写出来,相信你结合源码一下就能明白了:
先简单看看ThreadLocal源码:(在后面会为大家上图说明,并给出一个ThreadLocal简单用法)
public class ThreadLocal<T> {
    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();
    }
    ......

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    ......

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    ......
}

public class Thread implements Runnable {
    ......
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ......
}



总结的图如下:



  从图中可以看到,每个thread中都存在一个map,对此map作几下以点总结:
  1、由于map是属于当前线程的,自然在多线程环境中,其每一分ThreadLocal类型的变量的拷贝都是独立的,也就是线程安全的。这也是采用ThreadLocal变量与采用线程同步方式解决线程安全的最大区别——采用线程同步方式,类的成员变量作为多线程之间的共享资源;而采用ThreadLocal变量,根本就没有想到到共享,也不可能共享。

  2、map的类型是ThreadLocal的内部类ThreadLocalMap,为什么是内部类呢?这是因为该map要使用其外部类ThreadLocal的this指针作为key,这样可以保证同一个ThreadLocal类型的变量的拷贝只有唯一一份。另外注意个Map中的key是Threadlocal实例的弱引用,为什么使用弱引用呢?这个在后面为大家略作分析。

  3、ThreadLocalMap是ThreadLocal的静态内部类,为什么是静态呢?
threadLocals是定义在Thread类中的,如果不是静态内部类,那Thread类中定义这个变量还需要得到ThreadLocal类的实例,这是很不方便的。可能还有其它原因(没深入研究)。
另外,由于ThreadLocalMap是通过ThreadLocal变量的this指针作为key的,因此如果ThreadLocal变量不是同一个对象,那么拿到的ThreadLocal变量就是另一份拷贝。(后面的示例代码中会做简要说明)。

  4、因为ThreadLocalMap是独立于这个ThreadLocal变量的,所以在一个类中可以声明、定义多个ThreadLocal变量,这些ThreadLocal变量都会被确保是独立于线程的拷贝。

下面看一个例子:
public class TestThreadLocal {
    ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
    ThreadLocal<String> stringLocal = new ThreadLocal<String>();
 
     
    public void set() {
    	//key就是ThreadLocal变量自身(this)
    	System.out.println("set longLocal: " + "key=" + longLocal + ",value=" + Thread.currentThread().getId());
        longLocal.set(Thread.currentThread().getId());
        System.out.println("set stringLocal: "+ "key=" + stringLocal + ",value=" +  Thread.currentThread().getName());
        stringLocal.set(Thread.currentThread().getName());
    }
     
    public long getLong() {
    	System.out.print("getLongLocal: ");
        return longLocal.get();
    }
     
    public String getString() {
    	System.out.print("getStringLocal: ");
        return stringLocal.get();
    }
     
    public static void main(String[] args) throws InterruptedException {
        final TestThreadLocal test = new TestThreadLocal();
        printThreadName(); 
        test.set();
        System.out.println(test.getLong());
        System.out.println(test.getString());
     
         
        Thread thread1 = new Thread(){
            public void run() {
            	printThreadName(); 
                test.set();
                System.out.println(test.getLong());
                System.out.println(test.getString());
            };
        };
        thread1.start();
        thread1.join();
        
        Thread thread2 = new Thread(){
        	TestThreadLocal test2 = new TestThreadLocal();
            public void run() {
            	printThreadName(); 
                test2.set();
                System.out.println(test2.getLong());
                System.out.println(test2.getString());
            };
        };
        thread2.start();
        thread2.join();
    
        printThreadName(); 
        System.out.println(test.getLong());
        System.out.println(test.getString());
    }
    
    private static void printThreadName(){
    	System.out.println("-------------------"); 
        System.out.println("[IN Thread " + Thread.currentThread().getName() + "]"); 
    }
}


运行结果如下:
可以明显看出:main线程和Thread-0采用的key都是同一对象,因此在两个线程中使用同一key但保存了独立于线程的不同拷贝;Thread-1由于是重新new出来的TestThreadLocal,因此采用的key则是不同的key,所以取出来的是另一份拷贝。(因此如果要取到的是同一对象,必须要求其key是同一对象)
-------------------
[IN Thread main]
set longLocal: key=java.lang.ThreadLocal@11671b2,value=1
set stringLocal: key=java.lang.ThreadLocal@12452e8,value=main
getLongLocal: 1
getStringLocal: main
-------------------
[IN Thread Thread-0]
set longLocal: key=java.lang.ThreadLocal@11671b2,value=8
set stringLocal: key=java.lang.ThreadLocal@12452e8,value=Thread-0
getLongLocal: 8
getStringLocal: Thread-0
-------------------
[IN Thread Thread-1]
set longLocal: key=java.lang.ThreadLocal@1e4f7c2,value=9
set stringLocal: key=java.lang.ThreadLocal@145f0e3,value=Thread-1
getLongLocal: 9
getStringLocal: Thread-1
-------------------
[IN Thread main]
getLongLocal: 1
getStringLocal: main
  • 大小: 25.1 KB
分享到:
评论

相关推荐

    彻底理解ThreadLocal 1

    ThreadLocal是Java中用于线程局部变量的一个工具类,它允许在多线程环境下为每个线程创建独立的变量副本,从而避免了线程之间的数据共享和由此引发的并发问题。ThreadLocal不是一种线程同步机制,而是提供了一种线程...

    ThreadLocal应用示例及理解

    创建ThreadLocal实例时,通常会定义一个泛型参数,代表该线程局部变量的类型。例如: ```java ThreadLocal&lt;String&gt; threadLocal = new ThreadLocal(); ``` ### 设置和获取值 在ThreadLocal中,`set()`方法用于...

    ThreadLocal

    在上述代码中,`formatter`是一个ThreadLocal类型的变量,它会为每个线程创建一个`SimpleDateFormat`的实例。当调用`get()`方法时,如果没有对应的线程实例,则会通过`initialValue()`方法创建一个新的实例并返回。 ...

    java ThreadLocal多线程专属的变量源码

    java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多...

    正确理解ThreadLocal.pdf

    `ThreadLocal`是Java平台提供的一种线程局部变量的解决方案,它为每一个使用该变量的线程都提供了独立的变量副本,使得每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。这不同于普通的静态...

    ThreadLocal 内存泄露的实例分析1

    在 `LeakingServlet` 的 `doGet` 方法中,如果 `ThreadLocal` 没有设置值,那么会创建一个新的 `MyCounter` 并设置到 `ThreadLocal` 中。关键在于,一旦 `MyCounter` 被设置到 `ThreadLocal`,那么它将与当前线程...

    ThreadLocal的用处

    创建ThreadLocal变量非常简单,只需定义一个ThreadLocal类型的实例即可。例如: ```java ThreadLocal&lt;String&gt; threadLocalVar = new ThreadLocal(); ``` 要访问或设置线程局部变量的值,使用`set()`和`get()`...

    Java中ThreadLocal的设计与使用

    1. **创建ThreadLocal实例**:首先,你需要创建一个ThreadLocal类型的实例,就像其他任何Java对象一样。 ```java ThreadLocal&lt;String&gt; threadLocal = new ThreadLocal(); ``` 2. **设置值**:使用`set()`方法为当前...

    ThreadLocal简单Demo

    `ThreadLocal`并不是一个线程,而是一个线程局部变量的容器,每个线程都有自己的副本,互不干扰。这使得线程间的数据隔离成为可能,避免了共享状态带来的同步问题。 **工作原理** 1. **创建与初始化**: 当我们创建...

    java 简单的ThreadLocal示例

    首先,你需要创建一个ThreadLocal类型的实例,这将作为你在每个线程中存储值的容器。 ```java ThreadLocal&lt;String&gt; threadLocal = new ThreadLocal(); ``` 2. **设置线程局部变量的值:** 在需要的地方,你可以...

    ThreadLocal的几种误区

    ThreadLocal是Java编程中一种非常特殊的变量类型,它主要用于在多线程环境下为每个线程提供独立的变量副本,从而避免了线程间的数据共享和冲突。然而,ThreadLocal在理解和使用过程中容易产生一些误区,这里我们将...

    ThreadLocal相关

    当多个线程访问同一个 ThreadLocal 变量时,每个线程将拥有其自己的变量副本,而不是共享同一个变量。 ThreadLocal 的特点 1. 线程独立:ThreadLocal 变量在线程之间是独立的,每个线程都有其自己的变量副本。 2. ...

    Java并发编实践之ThreadLocal变量.doc

    Java并发编程实践中,ThreadLocal变量是一个非常重要的工具,它在JDK1.2版本就已经引入。ThreadLocal不是代表一个线程实例,而是一种线程局部变量的机制,它为每个线程提供了一个独立的变量副本,各个线程可以独立地...

    ThreadLocal原理及在多层架构中的应用

    **线程局部变量(ThreadLocal)是Java编程中一个非常重要的概念,主要用于在多线程环境中为每个线程提供独立的变量副本。ThreadLocal不是一种数据结构,而是一种解决线程间共享数据的方式,它提供了线程安全的局部...

    ThreadLocal_ThreadLocal源码分析_

    ThreadLocal是Java中的一个线程局部变量类,它为每个线程创建了一个独立的变量副本。这意味着每个线程都有自己的ThreadLocal变量,互不干扰,提供了线程安全的数据存储方式。ThreadLocal通常用于在多线程环境下为每...

    JDK的ThreadLocal理解(一)使用和测试

    通过创建ThreadLocal实例,我们可以为每个线程提供一个独立的变量副本,这些副本在各个线程之间互不影响,从而实现线程局部变量的功能。本文将深入探讨ThreadLocal的使用、原理以及一些实际应用中的测试案例。 ### ...

    ThreadLocal整理.docx

    在 ThreadLocal 中,set 方法的逻辑是先获取当前线程的存取副本变量的 map,然后计算出数组中的下标位置,然后获取对应的桶对象,如果桶为空,直接创建一个新的桶对象,并将 key 和 value 存储在其中。如果桶不为空...

    java中ThreadLocal详解

    而`ThreadLocal`则提供了另一种思路:为每个使用该变量的线程分配独立的副本,这样一来,每个线程拥有自己的变量副本,互不影响,从而避免了线程安全问题。 #### 二、ThreadLocal的数据结构及实现原理 `...

Global site tag (gtag.js) - Google Analytics