找回密码
 立即注册
首页 业界区 业界 浅谈ThreadLocal----每个线程一个小书包

浅谈ThreadLocal----每个线程一个小书包

毋峻舷 4 天前
ThreadLocal是什么?
thread是线程,local是本地的意思
字面意思是线程本地。
其实更通俗的理解是给每个线程设置一个缓存。这个缓存用来存储当前线程在未来的业务逻辑中需要执行到的变量。
我们先来看怎么用:
首先创建全局变量ThreadLocal,
各自启动一个线程任务:
线程任务将变量设置到缓存中。
线程任务需要用到缓存中的变量时,直接从缓存中取即可。
  1. 1 import java.util.concurrent.TimeUnit;
  2. 2
  3. 3 /**
  4. 4  * @discription
  5. 5  */
  6. 6 public class ThreadLocalLearn {
  7. 7     static ThreadLocal<String> threadLocal = new ThreadLocal<>();
  8. 8
  9. 9     public static void main(String[] args) {
  10. 10         Runnable r = new Runnable() {
  11. 11             @Override
  12. 12             public void run() {
  13. 13                 threadLocal<strong>.set</strong>(Thread.currentThread().getName());
  14. 14                 sayMyName();
  15. 15                 threadLocal.<strong>remove</strong>();
  16. 16             }
  17. 17
  18. 18             public void sayMyName() {
  19. 19                 for (int i = 0; i < 3; i++) {
  20. 20                     String name = threadLocal.<strong>get</strong>();
  21. 21                     System.out.println(Thread.currentThread().getName() + " say: im a thread, name:" + name);
  22. 22                     try {
  23. 23                         TimeUnit.SECONDS.sleep(3);
  24. 24                     } catch (Exception e) {
  25. 25                         //...
  26. 26                     }
  27. 27                 }
  28. 28             }
  29. 29         };
  30. 30         Thread t1 = new Thread(r);
  31. 31         t1.start();
  32. 32         Thread t2 = new Thread(r);
  33. 33         t2.start();
  34. 34     }
  35. 35 }
复制代码
它的使用非常简单,
(1)先set()存储值;
(2)使用时get()取出值;
(3)用完了使用remove()清理掉;
输出如下:
  1. Connected to the target VM, address: '127.0.0.1:56863', transport: 'socket'
  2. Thread-0 say: im a thread, name:Thread-0
  3. Thread-1 say: im a thread, name:Thread-1
  4. Thread-0 say: im a thread, name:Thread-0
  5. Thread-1 say: im a thread, name:Thread-1
  6. Thread-1 say: im a thread, name:Thread-1
  7. Thread-0 say: im a thread, name:Thread-0
  8. 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.png

 查看源码,我们可以发现如下特性:
1、ThreadLocal本身并不是缓存,它只是起到一个缓存的key 的作用。我们每次创建一个ThreadLocal 并不是真正的创建了一个缓存,其实只是创建了一个缓存的标识。
源码如下:this 就是ThreadLocal实例
  1. 1     public void set(T value) {
  2. 2         Thread t = Thread.currentThread();
  3. 3         ThreadLocalMap map = getMap(t);
  4. 4         if (map != null) {
  5. 5             map.set(this, value);
  6. 6         } else {
  7. 7             createMap(t, value);
  8. 8         }
  9. 9     }
复制代码
2、真正的缓存保存在Thread中,缓存被定义为:
ThreadLocal.ThreadLocalMap threadLocals;
从名字可以发现,这个缓存的类型是在ThreadLocal 中定义的一个静态内部类。这个类就是用来真正存放缓存的地方。这就像是thread小书包一样,每个线程有一个自己的独立的存储空间。
设计疑问:它(ThreadLocalMap)为什么没有定义在Thread类中,毕竟它是Thread的缓存。
源码如下:Thread.java
  1. 1     /* ThreadLocal values pertaining to this thread. This map is maintained
  2. 2      * by the ThreadLocal class. */
  3. 3     ThreadLocal.ThreadLocalMap threadLocals = null;
复制代码
 3、查看ThreadLocalMap的源码,我们发现它并没有实现Map接口,就像其他map一样,ThreadLocalMap实现了常用的Map中的set,get,getEntry,setThreshold,,remove 等方法。
并且它内部使用了线性探测法来解决哈希冲突。
设计疑问:它(ThreadLocalMap)为什么没有实现Map接口?
源码如下:ThreadLocal.Java
  1. 1 static class ThreadLocalMap {
  2. 2
  3. 3         //...
  4. 4
  5. 5         private static final int INITIAL_CAPACITY = 16;
  6. 6
  7. 7
  8. 8         private Entry[] table;
  9. 9
  10. 10
  11. 11         private int size = 0;
  12. 12
  13. 13
  14. 14         private int threshold; // Default to 0
  15. 15
  16. 16
  17. 17         private void setThreshold(int len) {
  18. 18             threshold = len * 2 / 3;
  19. 19         }
  20. 20
  21. 21
  22. 22         private Entry getEntry(ThreadLocal<?> key) {
  23. 23                  ...
  24. 24         }
  25. 25
  26. 26
  27. 27
  28. 28         private void set(ThreadLocal<?> key, Object value) {
  29. 29         ...
  30. 30         }
  31. 31
  32. 32
  33. 33         private void remove(ThreadLocal<?> key) {
  34. 34         ...
  35. 35         }
  36. 36
  37. 37
  38. 38         private void rehash() {
  39. 39             ...
  40. 40         }
  41. 41
  42. 42         private void resize() {
  43. 43            ...
  44. 44         }
  45. 45       ....
  46. 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

相关推荐

您需要登录后才可以回帖 登录 | 立即注册