看了些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是Java中用于线程局部变量的一个工具类,它允许在多线程环境下为每个线程创建独立的变量副本,从而避免了线程之间的数据共享和由此引发的并发问题。ThreadLocal不是一种线程同步机制,而是提供了一种线程...
创建ThreadLocal实例时,通常会定义一个泛型参数,代表该线程局部变量的类型。例如: ```java ThreadLocal<String> threadLocal = new ThreadLocal(); ``` ### 设置和获取值 在ThreadLocal中,`set()`方法用于...
在上述代码中,`formatter`是一个ThreadLocal类型的变量,它会为每个线程创建一个`SimpleDateFormat`的实例。当调用`get()`方法时,如果没有对应的线程实例,则会通过`initialValue()`方法创建一个新的实例并返回。 ...
java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多...
`ThreadLocal`是Java平台提供的一种线程局部变量的解决方案,它为每一个使用该变量的线程都提供了独立的变量副本,使得每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。这不同于普通的静态...
在 `LeakingServlet` 的 `doGet` 方法中,如果 `ThreadLocal` 没有设置值,那么会创建一个新的 `MyCounter` 并设置到 `ThreadLocal` 中。关键在于,一旦 `MyCounter` 被设置到 `ThreadLocal`,那么它将与当前线程...
创建ThreadLocal变量非常简单,只需定义一个ThreadLocal类型的实例即可。例如: ```java ThreadLocal<String> threadLocalVar = new ThreadLocal(); ``` 要访问或设置线程局部变量的值,使用`set()`和`get()`...
1. **创建ThreadLocal实例**:首先,你需要创建一个ThreadLocal类型的实例,就像其他任何Java对象一样。 ```java ThreadLocal<String> threadLocal = new ThreadLocal(); ``` 2. **设置值**:使用`set()`方法为当前...
`ThreadLocal`并不是一个线程,而是一个线程局部变量的容器,每个线程都有自己的副本,互不干扰。这使得线程间的数据隔离成为可能,避免了共享状态带来的同步问题。 **工作原理** 1. **创建与初始化**: 当我们创建...
首先,你需要创建一个ThreadLocal类型的实例,这将作为你在每个线程中存储值的容器。 ```java ThreadLocal<String> threadLocal = new ThreadLocal(); ``` 2. **设置线程局部变量的值:** 在需要的地方,你可以...
ThreadLocal是Java编程中一种非常特殊的变量类型,它主要用于在多线程环境下为每个线程提供独立的变量副本,从而避免了线程间的数据共享和冲突。然而,ThreadLocal在理解和使用过程中容易产生一些误区,这里我们将...
当多个线程访问同一个 ThreadLocal 变量时,每个线程将拥有其自己的变量副本,而不是共享同一个变量。 ThreadLocal 的特点 1. 线程独立:ThreadLocal 变量在线程之间是独立的,每个线程都有其自己的变量副本。 2. ...
Java并发编程实践中,ThreadLocal变量是一个非常重要的工具,它在JDK1.2版本就已经引入。ThreadLocal不是代表一个线程实例,而是一种线程局部变量的机制,它为每个线程提供了一个独立的变量副本,各个线程可以独立地...
**线程局部变量(ThreadLocal)是Java编程中一个非常重要的概念,主要用于在多线程环境中为每个线程提供独立的变量副本。ThreadLocal不是一种数据结构,而是一种解决线程间共享数据的方式,它提供了线程安全的局部...
ThreadLocal是Java中的一个线程局部变量类,它为每个线程创建了一个独立的变量副本。这意味着每个线程都有自己的ThreadLocal变量,互不干扰,提供了线程安全的数据存储方式。ThreadLocal通常用于在多线程环境下为每...
通过创建ThreadLocal实例,我们可以为每个线程提供一个独立的变量副本,这些副本在各个线程之间互不影响,从而实现线程局部变量的功能。本文将深入探讨ThreadLocal的使用、原理以及一些实际应用中的测试案例。 ### ...
在 ThreadLocal 中,set 方法的逻辑是先获取当前线程的存取副本变量的 map,然后计算出数组中的下标位置,然后获取对应的桶对象,如果桶为空,直接创建一个新的桶对象,并将 key 和 value 存储在其中。如果桶不为空...
而`ThreadLocal`则提供了另一种思路:为每个使用该变量的线程分配独立的副本,这样一来,每个线程拥有自己的变量副本,互不影响,从而避免了线程安全问题。 #### 二、ThreadLocal的数据结构及实现原理 `...