• 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
  • 相关阅读:
    设计模式--工厂模式
    docker
    学习的网址
    showslow小记
    .Net学习线路图
    《掌握软件测试九大技术》
    Apache和Tomcat
    Visual Studio 2017 更新到15.3后 提示错误:包含了重复的“Compile”项 的解决方法
    Asp.Net Core MVC项目实现多语言(Globalization/Localization)
    7月23日 会议纪要(项目有重要变动)
  • 原文地址:https://www.cnblogs.com/jxyedu/p/6257858.html
Copyright © 2020-2023  润新知