看了些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
分享到:
相关推荐
java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多...
并且由于每个线程在访问该变量时,读取和修改的,都是独有的那份变量拷贝,变量被彻底封闭在每个访问的线程中,并发错误出现的可能也完全消除了。} catch (SQL
ThreadLocal入门教程。 讲解了线程安全和ThreadLocal的使用的基本知识。
ThreadLocal
Java并发编实践之ThreadLocal变量.doc
理解ThreadLocal 理解ThreadLocal 理解ThreadLocal 理解ThreadLocal
ThreadLocal应用示例及理解,这个写了相关的示例,可以参考一下。
ThreadLocal保证一个类的实例变量在各个线程中都有一份单独的拷贝, 从而不会影响其他线程中的实例变量
学习ThreadLocal,了解其中的原理,以及学习其中的优点!避免坑点!!
ThreadLocal 线程微本地变量 及 源码分析
DbUTils中用ThreadLocal类
正确理解ThreadLocal.pdf
主要介绍ThreadLocal的原理,实例分析以及注意事项
ThreadLocal的几种误区ThreadLocal的几种误区ThreadLocal的几种误区
java 简单的ThreadLocal示例
Synchronized与ThreadLocal
关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. 关于线程变量ThreadLocal的介绍以及说明. ...
本例以序列号生成的程序为例,展示ThreadLocal的使用
设计模式及ThreadLocal详细讲解资料,想要学习java或者提升自己技术的同学可以下载观看