ThreadLoad


ThreadLocal作用

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

ThreadLoad 使用

API解释
new ThreadLocal<>();创建 ThreadLocal 对象
set(xxx)将 xxx 对象 放入 ThreadLocal 对象中
get()取出 当前线程 对应的 ThreadLocal 对象 存放的 值


ThreadLoad介绍

例子
tomcat每接收一个请求,都会开启一个线程,该线程完成的任务可能是一个事务控制操作,在一次请求中会调用多个service,多个DAO,我们希望在当中有个数据是共享的并且是线程安全的。当把所有事情做完后,最后commit

ThreadLoad就能很好的解决事务安全和事务一致性的问题

在这里插入图片描述
在这里插入图片描述

ThreadLoad 环境搭建

ThreadLoad:同一个线程中,共享数据并且是线程安全的

应用案例要实现的操作
在T1类中开启一个线程,在线程(thread0)中放入dog对象-->并且调用service中的方法可以得到thread0中存放的dog对象,并且调用DAO中的方法-->DAO可以得到thread0中的存放的dog对象

在整个过程中,都是同一个线程在执行操作。ThreadLoad中存放的数据,在同一个线程中都可以共享

在这里插入图片描述
T1

package com.study.threadlocal;

/**
 * @author 珀筱
 */
@SuppressWarnings({"all"})
public class T1 {
    //Task是线程类->内部类/线程
    public static class Task implements Runnable {
        @Override
        public void run() {
            Dog dog = new Dog();
            Pig pig = new Pig();
            System.out.println("在run方法中线程=" + Thread.currentThread().getName());
            new T1Service().update();
        }
    }

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

T1Service

package com.study.threadlocal;

/**
 * @author 珀筱
 */
public class T1Service {
    public void update() {
        //获取当前线程
        String name = Thread.currentThread().getName();
        System.out.println("在T1Service的update()线程name=" + name);
        //调用dao-update
        new T2DAO().update();
    }
}

T2DAO

package com.study.threadlocal;

/**
 * @author 珀筱
 */
public class T2DAO {
    public void update() {
        //获取当前线程名
        String name = Thread.currentThread().getName();
        System.out.println("在T2DAO的update()线程是= " + name);
    }
}

在这里插入图片描述

ThreadLoad使用

T1

package com.study.threadlocal;

/**
 * @author 珀筱
 */
@SuppressWarnings({"all"})
public class T1 {

    //创建Threadlocal对象,并设置为public static
    public static ThreadLocal<Object> threadLocal1 = new ThreadLocal<>();

    //Task是线程类->内部类/线程
    public static class Task implements Runnable {
        @Override
        public void run() {
            Dog dog = new Dog();
            Pig pig = new Pig();
            System.out.println( "Task放入了dog=" + dog);
            threadLocal1.set(dog);
            System.out.println("在run方法中线程=" + Thread.currentThread().getName());
            new T1Service().update();
        }
    }

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

T1Service

package com.study.threadlocal;

/**
 * @author 珀筱
 */
public class T1Service {
    public void update() {
        //取出threadLocal1对象关联的对象
        Object o = T1.threadLocal1.get();
        //获取当前线程
        String name = Thread.currentThread().getName();
        System.out.println("在T1Service的update()线程name=" + name + ",取出dog=" + o);
        //调用dao-update
        new T2DAO().update();
    }
}

T2DAO

package com.study.threadlocal;

/**
 * @author 珀筱
 */
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);
    }
}

同一个线程中,取出的对象确实是同一个
在这里插入图片描述

ThreadLoad源码分析

ThreadLoad类图分析

可以看出 ThreadLoad 下有一个 静态类部类 ThreadLocalMap(ThreadLocal.ThreadLocalMap)
ThreadLocalMap 有一个属性 table 为 Entry 数组
ThreadLocalMap 下有一个 静态类部类 Entry(ThreadLocal.ThreadLocalMap.Entry)
在这里插入图片描述

Thread属性 threadLocals分析

在Thread类中,有一个属性 threadLocals ,它的类型是 ThreadLocal.ThreadLocalMap
默认为 null
在这里插入图片描述

Dbug查看set方法

threadLocal1 对象 = {ThreadLocal@547}
在这里插入图片描述
Thread0 的 threadLocals 属性 下的 table 下 目前只有两个默认的值
在这里插入图片描述

点击下一步进入数据
Thread0 的 threadLocals 属性 下的 table 下 加入了 一个数据
在这里插入图片描述
查看table表中的数据,可以发现
将当前ThreadLocal 和 传入的数据 存放到了 table 中
table表 为 Entry 数组 , 由此得出 set方法是将 当前threadlocal 和 传入的数据 为一对数据 存放到 entry 中
在这里插入图片描述

set方法源码分析

  • 1. 进入set方法后,先获取当前线程 Thread t = Thread.currentThread()
  • 2. 通过线程,获得ThreadLocalMap ThreadLocalMap map = getMap(t);
    • 2.1 获得map方法,本质就是 返回当前线程的threadLocals属性 return t.threadLocals
  • 3. 判断map是否为空
    • 3.1 如果map不为空。将当前 threadlocal 和 value 以 entry 的形式 保存在 ThreadLocalMap 的 table 中
    • 3.2 如果map为空。将创建 ThreadLocalMap createMap(t, value);
      • 3.2.1 创建ThreadLocalMap,本质就是创建一个ThreadLocalMap 并赋值给 当前的线程的 threadLocals t.threadLocals = new ThreadLocalMap(this, firstValue);
      • 3.2.2 new ThreadLocalMap(this, firstValue),本质就是给ThreadLocalMap 的 table 属性分配空间,并将 当前 ThreadLocal 和 value 以 Entry 的形式保存在table中


通过上面分析:

  1. ThreadLocalMap 保存在 当前的线程的 threadLocals 属性中,这两的类型都是Threadlocal.ThreadLocalMap,当创建完ThreadLocalMap 都会 保存在当前线程中
  2. ThreadLocal只能保存一个数据,因为在保存数据时,是以ThreadLocal为key,key重复是会替换value的
  • 当前线程下有一个 threadLocals属性
  • threadLocals 属性 本质就是 threadLocal 下的静态类部类 ThreadLocalMap,每次创建ThreadLocalMap都会赋值给 threadLocals 属性
  • ThreadLocalMap 下 有一个 table属性,table属性 本质就是 Entry数组
  • ThreadLocalMap 下 有一个 静态类部类 Entry。Entry只能存放一对数据,key 为当前ThreadLocal,value 为传进来的数据 并 存放到 table属性中(通过当前 ThreadLocal 的 hashcode 计算出 存放位置)

public void set(T value)

            public void set(T value) {
                //获取当前线程
                Thread t = Thread.currentThread();
                //通过当前线程获取 当前线程对应的map
                ThreadLocalMap map = getMap(t);
                //如果map不为空,将当前threadlocal为key,传进来的数据为value放入map中
                // 从这可以看出 一个threadlocal只能存放一个value,如果对threadlocal多次set,就会替换value
                if (map != null)
                    map.set(this, value);
                //如果map为空(第一次存放数据), 创建与当前线程对应map,
                else
                    createMap(t, value);
             }

ThreadLocalMap getMap(Thread t)

            ThreadLocalMap getMap(Thread t) {
            //返回当前线程的threadLocals
               return t.threadLocals;
            }

void createMap(Thread t, T firstValue)

            void createMap(Thread t, T firstValue) {
            //创建一个与当前线程有关的ThreadLocalMap对象(静态内部类)
            // 并赋值给当前线程的threadLocals
                t.threadLocals = new ThreadLocalMap(this, firstValue);
            }

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue)

            ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
                //创建一个Entry数组table,默认大小为16
                table = new Entry[INITIAL_CAPACITY];
                //根据hash值计算存放位置
                int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
                //以当前threadlocal为key,传入的数据为value创建Entry 放入 table中
                table[i] = new Entry(firstKey, firstValue);
                size = 1;
                //判断是否需要对table扩容
                setThreshold(INITIAL_CAPACITY);
        }


get方法源码分析

  • 1. 进入get方法后,先获取当前线程 Thread t = Thread.currentThread();
  • 2. 通过当前线程,获取ThreadLocalMap ThreadLocalMap map = getMap(t);
    • 2.1 获取ThreadLocalMap,本质就是返回当前线程的 threadLocals 对象 return t.threadLocals;
  • 3. 判断 map 是否为空
    • 3.1 如果 map 不为空。通过当前 ThreadLocal 获得对应的Entry ThreadLocalMap.Entry e = map.getEntry(this);
      • 3.1.1 map.getEntry(this);,本质就是 通过 当前 ThreadLocal 的 hashcode 计算出 当前 ThreadLocal 存放在 table 中的 位置 并 取出 在该位置的 Entry--> Entry如果不为空 并 Entry的key值与当前 ThreadLocal 相等 -->返回Entry-->否则返回null
    • 3.2 map为空。返回null


先获取线程 -->通过线程获得ThreadLocalMap(threadLocals)-->通过 ThreadLocalMap 和 当前 ThreadLocal 获得 对应 Entry --> 通过Entry 获得 value

public T get()

            public T get() {
                //获取当前线程
                Thread t = Thread.currentThread();
                //通过当前线程,获取ThreadLocalMap
                ThreadLocalMap map = getMap(t);
                //如果map不为空
                if (map != null) {
                    //通过当前 threadlocal获得对应的Entry
                    ThreadLocalMap.Entry e = map.getEntry(this);
                    //如果Entry不为空
                    if (e != null) {
                        //返回当前 threadlocal对应的 value
                        @SuppressWarnings("unchecked")
                        T result = (T)e.value;
                        return result;
                    }
                }
                //进行初始化,返回null
                return setInitialValue();
            }

ThreadLocalMap getMap(Thread t)

            ThreadLocalMap getMap(Thread t) {
                //返回 当前线程的 threadLocals 属性
                return t.threadLocals;
            }

private Entry getEntry(ThreadLocal<?> key)

            private Entry getEntry(ThreadLocal<?> key) {
                //通过threadlocal的HashCode 计算出当前threadlocal 存放在 table 的位置
                int i = key.threadLocalHashCode & (table.length - 1);
                //取出 当前threadlocalHashCode 对应的 Entry
                Entry e = table[i];
                //Entry对象不为空,且 Entry的key 为 当前 ThreadLocal,返回Entry
                if (e != null && e.get() == key)
                    return e;
                else  //返回null
                    return getEntryAfterMiss(key, i, e);
            }



在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值