02_ThreadLocal语法与源码分析

文章导读:早在JDK 1.2的版本中就提供Java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序, 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本, 每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本. 视频与源码下载:http://edu.51cto.com/lecturer/index/user_id-9166337.html  (代码在视频的附件中) 先来用代码来演示下ThreadLocal强大特性
 1 // 用来来存储与线程相关的变量
 2 public class ThreadLocDemo {
 3     // 用来存储线程名称
 4     private ThreadLocal<String> ts = new ThreadLocal<String>();
 5     // 用来存储线程的ID
 6     private ThreadLocal<Long> tl = new ThreadLocal<Long>();
 7 
 8     // 获取当前线程的名称和ID信息
 9     public void set() {
10         ts.set(Thread.currentThread().getName());
11         tl.set(Thread.currentThread().getId());
12     }
13 
14     public String getName() {
15         return ts.get();
16     }
17 
18     public Long getId() {
19         return tl.get();
20     }
21 
22     public static void main(String[] args) throws Exception {
23         // 当前线程是主线程
24         // System.out.println(Thread.currentThread().getName());
25         // System.out.println(Thread.currentThread().getId());
26 
27         // 把主线程的信息存储在ThreadLocal中
28         final ThreadLocDemo demo = new ThreadLocDemo();
29         demo.set();
30         // 输出主线程的信息
31         System.out.println(demo.getName());
32         System.out.println(demo.getId());
33 
34         // 创建一个子线程,然后用demo也存储子线程的信息
35         new Thread(new Runnable() {
36             public void run() {
37                 // 把子线程的信息存储在ThreadLocal中
38                 demo.set();
39                 // 输出子线程的信息
40                 System.out.println(demo.getName());
41                 System.out.println(demo.getId());
42             }
43         }, "xyz").start();
44 
45         Thread.sleep(1000);
46         // 输出主线程的信息
47         System.out.println(demo.getName());
48         System.out.println(demo.getId());
49     }
50 }
从打印结果可以看出主线程与子线程数据的存储,获取是不冲突的
main
1
xyz
9
main
1
ThreadLocal.set源码分析, 通过源码可以看出来,Local为不同的线程创建了自己的ThreadLocalMap对象.数据本质是存储在ThreadLocalMap中
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     }
ThreadLocalMap存储结构分析
 1 static class ThreadLocalMap {
 2         // 存储数据的类型是弱引用
 3         static class Entry extends WeakReference<ThreadLocal> {
 4            
 5             Object value;
 6             Entry(ThreadLocal k, Object v) {
 7                 super(k);
 8                 value = v;
 9             }
10         }
11 }
ThreadLocal.get()源码分析. 其实还是先通过线程获取每个线程自己的LocalMap对象,然后从Map对象中获取数据信息
 1 public T get() {
 2         Thread t = Thread.currentThread();
 3         ThreadLocalMap map = getMap(t);
 4         if (map != null) {
 5             ThreadLocalMap.Entry e = map.getEntry(this);
 6             if (e != null)
 7                 return (T)e.value;
 8         }
 9         return setInitialValue();
10     }
 总结, 大家注意三点:
  • 每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象
  • 将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中
  • ThreadLocal 不是用来解决共享对象的多线程访问问题的, 它是用来延迟变量的生命周期,后面的事务、缓存都会用到此API
  • 相关内容推荐