ThreadLocal是什么?
thread是线程,local是本地的意思
字面意思是线程本地。
其实更通俗的理解是给每个线程设置一个缓存。这个缓存用来存储当前线程在未来的业务逻辑中需要执行到的变量。
我们先来看怎么用:
首先创建全局变量ThreadLocal,
各自启动一个线程任务:
线程任务将变量设置到缓存中。
线程任务需要用到缓存中的变量时,直接从缓存中取即可。- 1 import java.util.concurrent.TimeUnit;
- 2
- 3 /**
- 4 * @discription
- 5 */
- 6 public class ThreadLocalLearn {
- 7 static ThreadLocal<String> threadLocal = new ThreadLocal<>();
- 8
- 9 public static void main(String[] args) {
- 10 Runnable r = new Runnable() {
- 11 @Override
- 12 public void run() {
- 13 threadLocal<strong>.set</strong>(Thread.currentThread().getName());
- 14 sayMyName();
- 15 threadLocal.<strong>remove</strong>();
- 16 }
- 17
- 18 public void sayMyName() {
- 19 for (int i = 0; i < 3; i++) {
- 20 String name = threadLocal.<strong>get</strong>();
- 21 System.out.println(Thread.currentThread().getName() + " say: im a thread, name:" + name);
- 22 try {
- 23 TimeUnit.SECONDS.sleep(3);
- 24 } catch (Exception e) {
- 25 //...
- 26 }
- 27 }
- 28 }
- 29 };
- 30 Thread t1 = new Thread(r);
- 31 t1.start();
- 32 Thread t2 = new Thread(r);
- 33 t2.start();
- 34 }
- 35 }
复制代码 它的使用非常简单,
(1)先set()存储值;
(2)使用时get()取出值;
(3)用完了使用remove()清理掉;
输出如下:- Connected to the target VM, address: '127.0.0.1:56863', transport: 'socket'
- Thread-0 say: im a thread, name:Thread-0
- Thread-1 say: im a thread, name:Thread-1
- Thread-0 say: im a thread, name:Thread-0
- Thread-1 say: im a thread, name:Thread-1
- Thread-1 say: im a thread, name:Thread-1
- Thread-0 say: im a thread, name:Thread-0
- Disconnected from the target VM, address: '127.0.0.1:56863', transport: 'socket'
复制代码 很多人第一次见到ThreadLocal,第一直觉它的实现是用Map 。(防盗连接:本文首发自http://www.cnblogs.com/jilodream/ )但是深入研究之后,你会发现threadLocal的实现要比这样一个map 精妙的多,也好用的多。
我们通过查看java源码,可以依次探索ThreadLocal是如何实现缓存的:
类整体的关系大概是这样的:
查看源码,我们可以发现如下特性:
1、ThreadLocal本身并不是缓存,它只是起到一个缓存的key 的作用。我们每次创建一个ThreadLocal 并不是真正的创建了一个缓存,其实只是创建了一个缓存的标识。
源码如下:this 就是ThreadLocal实例- 1 public void set(T value) {
- 2 Thread t = Thread.currentThread();
- 3 ThreadLocalMap map = getMap(t);
- 4 if (map != null) {
- 5 map.set(this, value);
- 6 } else {
- 7 createMap(t, value);
- 8 }
- 9 }
复制代码 2、真正的缓存保存在Thread中,缓存被定义为:
ThreadLocal.ThreadLocalMap threadLocals;
从名字可以发现,这个缓存的类型是在ThreadLocal 中定义的一个静态内部类。这个类就是用来真正存放缓存的地方。这就像是thread小书包一样,每个线程有一个自己的独立的存储空间。
设计疑问:它(ThreadLocalMap)为什么没有定义在Thread类中,毕竟它是Thread的缓存。
源码如下:Thread.java- 1 /* ThreadLocal values pertaining to this thread. This map is maintained
- 2 * by the ThreadLocal class. */
- 3 ThreadLocal.ThreadLocalMap threadLocals = null;
复制代码 3、查看ThreadLocalMap的源码,我们发现它并没有实现Map接口,就像其他map一样,ThreadLocalMap实现了常用的Map中的set,get,getEntry,setThreshold,,remove 等方法。
并且它内部使用了线性探测法来解决哈希冲突。
设计疑问:它(ThreadLocalMap)为什么没有实现Map接口?
源码如下:ThreadLocal.Java- 1 static class ThreadLocalMap {
- 2
- 3 //...
- 4
- 5 private static final int INITIAL_CAPACITY = 16;
- 6
- 7
- 8 private Entry[] table;
- 9
- 10
- 11 private int size = 0;
- 12
- 13
- 14 private int threshold; // Default to 0
- 15
- 16
- 17 private void setThreshold(int len) {
- 18 threshold = len * 2 / 3;
- 19 }
- 20
- 21
- 22 private Entry getEntry(ThreadLocal<?> key) {
- 23 ...
- 24 }
- 25
- 26
- 27
- 28 private void set(ThreadLocal<?> key, Object value) {
- 29 ...
- 30 }
- 31
- 32
- 33 private void remove(ThreadLocal<?> key) {
- 34 ...
- 35 }
- 36
- 37
- 38 private void rehash() {
- 39 ...
- 40 }
- 41
- 42 private void resize() {
- 43 ...
- 44 }
- 45 ....
- 46 }
复制代码 4、继续看源码,我们发现ThreadLocalMap类像其他Map实现一样,在内部定义了Entry。并且这个Entry居然继承了弱引用,弱引用被定义在Entry的key上,而且key的类型是ThreadLocal。
至于什么是弱引用,我以前的文章中介绍过,请看(浅谈Java中的引用 https://www.cnblogs.com/jilodream/p/6181762.html),一定要对弱引用了解,否则ThreadLocal的核心实现以及它会存在的问题,就无法更深理解了。
这里又会有疑问,为什么要使用弱引用,使用强引用不好吗?弱引用万一被回收导致空引用等问题怎么办?
源码如下:ThreadLocal.Java
[code]1 static class Entry extends WeakReference |