18. 线程数据共享和安全 -ThreadLocal


18.1 什么是 ThreadLocal

  1. ThreadLocal 的作用,可以实现在同一个线程数据共享,从而解决多线程数据安全问题
  2. ThreadLocal 可以给当前线程关联一个数据 (普通变量、对象、数组) set 方法
  3. ThreadLocal 可以像 Map 一样存取数据,key 为当前线程,get 方法
  4. 每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个 ThreadLocal 对象实例
  5. 每个 ThreadLocal 对象实例定义的时候,一般为 static 类型
  6. ThreadLocal 中保存数据,在线程销毁后,会自动释放

在这里插入图片描述

18.2 快速入门 ThreadLocl

● 需求: 演示 ThreadLocal (作用在一个线程中, 共享数据(线程安全))的使用-画图


在这里插入图片描述


● 代码实现

  1. 创建 Dog.java
package com.xjs.threadlocal;

/**
 * @Author: 谢家升
 * @Date: 2022/3/28-03-28-21:41
 * @Version: 1.0
 */
public class Dog {
}

  1. 创建 Pig.java
package com.xjs.threadlocal;

/**
 * @Author: 谢家升
 * @Date: 2022/3/28-03-28-21:41
 * @Version: 1.0
 */
public class Pig {
}

  1. 创建 T2DAO.java
package com.xjs.threadlocal;

/**
 * @Author: 谢家升
 * @Date: 2022/3/28-03-28-21:42
 * @Version: 1.0
 */
public class T2DAO {

    public void update() {
        //取出 threadLocal1 对象关联的对象数据
        Object o = T1.threadLocal1.get();

        //获取当前线程名称
        String name = Thread.currentThread().getName();
        System.out.println("在 T2DAO 的 update() 线程是= " + name + " 取出的dog= " + o);
    }

}

  1. 创建 T1Service.java
package com.xjs.threadlocal;

/**
 * @Author: 谢家升
 * @Date: 2022/3/28-03-28-21:43
 * @Version: 1.0
 */
public class T1Service {

    public void update() {
        //取出 threadLocal1 对象关联的dog对象

        /**
         * 解读 :threadLocal1.get();
         *
         *     public T get() {
         *          //1.先得到当前的线程对象
         *         Thread t = Thread.currentThread();
         *         //2.通过当前线程获取到对应的 ThreadLocalMap
         *         ThreadLocalMap map = getMap(t);
         *         if (map != null) {
         *              //3.如果这个 map不为null,就根据当前调用者(此处就是threadLocal1对象),得到对应的Entry
         *             ThreadLocalMap.Entry e = map.getEntry(this);
         *             if (e != null) {
         *                 @SuppressWarnings("unchecked")
         *                 //4.如果 Entry 不为null,就返回当前 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 + " dog=" + o);
        //调用了 T2DAO 的 update() 方法
        new T2DAO().update();

    }

}

  1. 创建 T1.java
package com.xjs.threadlocal;

import java.security.PublicKey;

/**
 * @Author: 谢家升
 * @Date: 2022/3/28-03-28-21:21
 * @Version: 1.0
 */
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);

            /*
                阅读源码:threadLocal1.set(dog);

                public void set(T value) {
                    //1. 获取当前线程,关联到当前线程
                    Thread t = Thread.currentThread();
                    //2. 通过线程对象 t ,获取到 ThreadLocalMap
                    //   ThreadLocalMap 是 threadLocal1 里面的一个静态内部类
                    ThreadLocalMap map = getMap(t);
                    //3. 如果 map 不为null,将 数据(dog,pig...) 放入 map,
                    //   map 里面的--key:当前线程threadLocal  value:要存放的数据
                    //   从这个源码我们可以看出,一个threadLocal只能关联一个数据,如果再set(),就会替换
                    if (map != null)
                        map.set(this, value);
                    else
                        //4. 如果 map 为null,就创建一个和当前线程关联的ThreadLocalMap,并且将该数据放入
                        createMap(t, value);
                }

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

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

}

  1. 完成测试, 检测 ThreadLocal 是否可以实现在同一个线程数据安全共享

在这里插入图片描述

18.3 ThreadLocal 源码解读+画图

18.4 ThreadLocal 原理分析图

  1. ThreadLocal 原理分析图【重点 set 和 get】

在这里插入图片描述


在这里插入图片描述


  1. Debug 源码图,非常重要

在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

要学就学灰太狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值