当前位置: 首页 > article >正文

ThreadLocal机制解读和源码分析

目录

线程数据共享和安全 -ThreadLocal

什么是 ThreadLocal

代码演示

创建Dog.java

创建Pig.java

T2DAO.java

T2DAO

T1解读set

T1Service  解读 get

ThreadLocalTest这个是换一种法

ThreadLocal 原理分析图

1. ThreadLocal 原理分析图(重点 set 和 get)



线程数据共享和安全 -ThreadLocal

什么是 ThreadLocal

1. ThreadLocal 的作用,可以实现在同一个线程数据共享, 从而解决多线程数据安全问题.

2. ThreadLocal 可以给当前线程关联一个数据(普通变量、对象、数组)set 方法 [源码!]

3. ThreadLocal 可以像 Map 一样存取数据,key 为当前线程, get 方法

4. 每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个 ThreadLocal 对象实例

5. 每个 ThreadLocal 对象实例定义的时候,一般为 static 类型

6. ThreadLocal 中保存数据,在线程销毁后,会自动释放

代码演示

创建Dog.java

public class Dog { }

创建Pig.java

public class Pig{ }

T2DAO.java

public class T2DAO {
    public void update() {
        String name = Thread.currentThread().getName();
        System.out.println("在 T2DAO 的 update() 线程是= " + name);
    }
}

T2DAO

public class T2DAO {
    public void update() {
        String name = Thread.currentThread().getName();
        System.out.println("在 T2DAO 的 update() 线程是= " +
                name + " threadlocal 数据是=" + ThreadLocalTest.threadLocal.get());
        System.out.println("在 T2DAO 的 update() 线程是= " +
                name + " threadlocal2 数据是=" + ThreadLocalTest.threadLocal2.get());
    }
}

T1解读set

1. 获取当前线程, 关联到当前线程!

2. 通过线程对象, 获取到ThreadLocalMap其中ThreadLocalMap是ThreadLocal的内部类

其中Entry又是ThreadLocalMap的内部类是存放真正的数据的地方 这同样也是为什么是一个线程只能存放一个对象 同样的线程放入会被覆盖

package com.threadlocal;

public class    T1 {

    //创建ThreadLocal对象, 做成public static.
    public static ThreadLocal<Object> threadLocal1 = new ThreadLocal<>();
    public static ThreadLocal<Object> threadLocal2 = new ThreadLocal<>();

    //Task 是线程类 -> 内部类 / 线程
    public static class Task implements Runnable {
        @Override
        public void run() {
            Dog dog = new Dog();
            Pig pig = new Pig();
            //给threadLocal1 对象放入set dog , 隔山打牛
            System.out.println("Task 放入了 dog= " + dog);
            /*
                解读
                public void set(T value) {
                    //1. 获取当前线程, 关联到当前线程!
                    Thread t = Thread.currentThread();
                    //2. 通过线程对象, 获取到ThreadLocalMap
                    //   ThreadLocalMap 类型 ThreadLocal.ThreadLocalMap
                    ThreadLocalMap map = getMap(t);
                    //3. 如果map不为null, 将数据(dog,pig..) 放入map -key:threadLocal value:存放的数据
                    //   从这个源码我们已然看出一个threadlocal只能关联一个数据,如果你set, 就会替换
                    //4. 如果map为null, 就创建一个和当前线程关联的ThreadLocalMap, 并且该数据放入
                    if (map != null)
                        map.set(this, value);
                    else
                        createMap(t, value);
                }

             */
            threadLocal1.set(dog);
            //threadLocal1.set(pig);//替换
            threadLocal2.set(pig);//这个数据就会threadLocal2关联,并且都被当前Thread管理
            System.out.println("Task 在run 方法中 线程=" + Thread.currentThread().getName());
            new T1Service().update();
        }
    }

    public static void main(String[] args) {
        new Thread(new Task()).start();//主线程启动一个新的线程,注意不是主线程
    }
}

T1Service  解读 get

1. 先得到当前的线程对象

2.通过线程获取到对应的ThrealLocalMap

3. 如果map不为空, 根据当前的 threadlocal对象,得到对应的Entry

4. 如果e 不为null就继续下面的

public class T1Service {

    public void update(){
        //取出threadLocal1对象关联的对象
        /**
         * 解读 get
         * public T get() {
         *          //1. 先得到当前的线程对象
         *         Thread t = Thread.currentThread();
         *         //2.通过线程获取到对应的ThrealLocalMap
         *         ThreadLocalMap map = getMap(t);
         *         if (map != null) {
         *              //3. 如果map不为空, 根据当前的 threadlocal对象,得到对应的Entry
         *             ThreadLocalMap.Entry e = map.getEntry(this);
         *             //4. 如果e 不为null
         *             if (e != null) {
         *                 @SuppressWarnings("unchecked")
         *                 //返回当前threadlocal关联的数据value
         *                 T result = (T)e.value;
         *                 return result;
         *             }
         *         }
         *         return setInitialValue();
         *     }
         *
         */
        Object o = T1.threadLocal1.get();
        //获取当前线程名
        String name = Thread.currentThread().getName();
        System.out.println("在T1Service的update() 线程name= " +
                name + " dog= " + o);
        //调用dao-update
        new T2DAO().update();
    }
}

ThreadLocalTest这个是换一种法

public class ThreadLocalTest {
    public static ThreadLocal<Object> threadLocal = new ThreadLocal<>();
    public static ThreadLocal<Object> threadLocal2 = new ThreadLocal<>();
    public static class Task implements Runnable {
        @Override
        public void run() {
            Dog dog = new Dog();
            Pig pig = new Pig();
                /*
                 * ======== set 源码分析 只要明白这个机制,后面的 set get 全部通透
           
                  public void set(T value) {
                 获取当前线程
                  Thread t = Thread.currentThread();
                  获取当前线程的 ThreadLocal.ThreadLocalMap 属性 threadLocals , 类型是 ThreadLocal 的静态内部类
                 //threadLocals 有 一 个 属 性 Entry[], 类 型
                 ThreadLocal.ThreadLocalMap.Entry
                  //k-> ThreadLocal 对象   v-> 值
                  ThreadLocalMap map = getMap(t);
                  if (map != null)
                  map.set(this, value);//存放这里的 this 就是 ThreadLocal, 可以
                 debug 源码,一目了然
                  else
                  createMap(t, value);//创建
                  }
                 
                 * ====== getMap 方法源码=====
                  ThreadLocalMap getMap(Thread t) { // 获取当前线程的
                 ThreadLocal.ThreadLocalMap
                  return t.threadLocals;
                  }
                 
                  说明:
                  1. ThreadLocalMap 对象是和当前 Thread 对象的绑定的属性
                  2. ThreadLocalMap 对象含有 Entry[] table; 这个 Entry(K,V)
                  3. 这个 key 就是 ThreadLocal 对象, V 就是你要在放入的对象,比如 dog
                  4. 当执行了 了 threadLocal.set(dog) 后,内存布局图为 wps[看图]
                 
                 */
            threadLocal.set(dog);
            threadLocal.set(pig);//会替换 dog
            //如果希望在同一个线程共享多个对象/数据,就在创建一个 ThreadLocal 对象
            //threadLocal2.set(pig);
            System.out.println("在 run 方法中 线程=" +
                    Thread.currentThread().getName() + " 放入 threadLocal 的数据=" + dog);
            new T1Service().update();
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 1; i++) {
            
            new Thread(new Task()).start();//启动一个新的线程,注意不是主线程
        }
        System.out.println("在 main 方法中 threadLocal 的数据=" + threadLocal.get());
    }
}

ThreadLocal 原理分析图

1. ThreadLocal 原理分析图(重点 set 和 get)

2. Debug 源码图

 


http://www.kler.cn/a/14920.html

相关文章:

  • 用好ChatGPT,毕业直接走上人生巅峰
  • 打造APP广告变现利器,了解聚合广告SDK的选择技巧与优势
  • 读 AI学者生存策略
  • 人工智能前沿——「小海带」超全视觉注意力机制资源分享(附下载链接)
  • 计算机网络学习07(DNS域名系统详解)
  • 制作自己的镜像并且推送到docker hub上去。
  • 10 | Qt处理16进制
  • Android Overlay机制
  • 日撸 Java 三百行day39
  • Docker-Compose 了解 部署nginx与lnmp
  • 组态王与PLC之间1主多从自组网无线通信
  • C++ 探索程序 详细解读程序在运行过程中都发生了什么
  • Maya - 后缀为xgen文件导出到虚幻引擎
  • 安卓GB28181-2022 RTP over TCP
  • Spring MVC开发及使用(8000字详解)
  • Springboot分布式项目优化策略
  • 聚观早报|中国将是ChatGPT主要对手;​iPhone 15将使用USB-C接口
  • Linux 查看进程和线程CPU和内存占用情况
  • 基于Vue的web设计打印方案
  • 登录页面jwt密钥,过滤器,拦截器,异常处理