Java中ThreadLocal的使用

 更新时间:2023年09月26日 09:02:06   作者:手写情书  
这篇文章主要介绍了Java中ThreadLocal的使用,静态内部类的加载是在程序中调用静态内部类的时候加载的,和外部类的加载没有必然关系, 但是在加载静态内部类的时候 发现外部类还没有加载,那么就会先加载外部类 ,加载完外部类之后,再加载静态内部类,需要的朋友可以参考下

1.预备知识-静态内部类

静态内部类的加载时机?他和外部类的加载有没有什么关系

静态内部类的加载是在程序中调用静态内部类的时候加载的,和外部类的加载没有必然关系, 但是在加载静态内部类的时候 发现外部类还没有加载,那么就会先加载外部类 ,加载完外部类之后,再加载静态内部类.(初始化静态变量和静态代码块etc),如果在程序中单纯的使用 外部类,并不会触发静态内部类的加载

扩展 :

一个类内部有静态内部类和非静态内部类 , 静态内部类和非静态内部类一样,都是在被调用时才会被加载 ,不过在加载静态内部类的过程中如果没有加载外部类,也会加载外部类,静态变量,静态方法,静态块等都是类级别的属性,而不是单纯的对象属性.他们在类第一次被使用时被加载 (记住,是一次使用,不一定是实例化),我们可以简单得用 类名.变量 或者 类名.方法来调用它们, 与调用没有被static 修饰过变量和方法不同的是:一般变量和方法是用当前对象的引用(即this)来调用的, 静态的方法和变量则不需要.从一个角度上来说,它们是共享给所有对象的,不是一个角度私有. 这点上,静态内部类也是一样的

类的加载时机:(暂时的认知里是四种) new 一个类的时候,调用类内部的 静态变量,调用类的静态方法,调用类的 静态内部类

public class OuterClass {
    public static String OUTER_DATE = "外部类静态变量加载时间 "+System.currentTimeMillis();
    static {
        System.out.println("外部类静态块加载时间:" + System.currentTimeMillis());
    }
    public OuterClass() {
        System.out.println("外部类构造函数时间:" + System.currentTimeMillis());
    }
    static class InnerStaticClass{
        public static String INNER_STATIC_DATE = "静态内部类静态变量加载时间 "+System.currentTimeMillis();
        private String name;
        static {
            System.out.println("静态内部类静态代码块加载时间:" + System.currentTimeMillis());
        }
    }
    class InnerClass {
        public String INNER_DATE = "";
        public InnerClass() {
            INNER_DATE = "非静态内部类构造器加载时间"+System.currentTimeMillis();
        }
    }
    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        System.out.println("非静态内部类加载时间: "+outer.new InnerClass().INNER_DATE);
        /**
         * 内部静态类可以直接用,不需要new
         * 静态内部类的加载是代码中需要静态内部类的时候才加载,而不是和外部类一起加载的
         * 加载静态内部类之前,先把外部类的静态变量和静态代码块先执行完
         * 执行完外部类的代码后,再执行静态内部类的 静态变量和静态代码块
         * 静态内部类的 静态变量和静态代码块执行完后,然后执行业务代码
         * new  外部类的时候 。外部类的静态代码块和静态变量先执行,外部类构造函数后执行
         */
        System.out.println("静态内部类加载时间____:"+InnerStaticClass.INNER_STATIC_DATE);
    }
}
//什么时候考虑使用静态内部类?
//A类中需要一个B类,但是B类只为A类服务,这种情况不需要将B类单独剥离,只需要在A内部即可
//一个类的构建有非常多参数或者十分复杂的一个对象的时候--引申到了建造者模式

2.ThredLocal

2.1ThrealLocal简介

ThrealLocal叫做现成变量,意思就是ThradLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量.ThradLocal为变量在每个线程中都创建了副本,那么每个线程可访问自己内部的副本变量.

  • 因为每个Thread内有自己的实例副本,且副本只能由当前Thread使用.这也是ThreadLocal命名的由来
  • 既然每个Thred都有自己的实例副本,且其他Thread不可访问,那么不存在多线程的共享问题

总的来说,ThredLocal适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程隔离而在方法或类间共享的场景

在这里插入图片描述

2.2 ThreadLocal与Synchronized的区别

ThreadLocal其实是与线程绑定的一个变量.ThreadLocal和Synchonized都用于解决多线程并发访问

但是ThreadLocal与synchronized有本质的区别 :

  • Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离
  • Synchronized是利用锁的机制,使变量或代码块在某一时刻只能被一个线程访问.而ThredLocal为每一个线程提供了变量的副本,使得每个线程某一个时间访问到的不是同一个对象,这样就隔离了多个现线程岁数据的共享, 而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享

一句话理解ThreadLocal,向ThreadLocal里面存东西就是向它里面的Map存东西的,然后ThreadLocal把这个Map挂到当前的线程底下,这样Map就只属于这个线程了

2.3 ThreadLocal的简单使用

public class ThreadLocaDemo {
    private static ThreadLocal<String> localVar = new ThreadLocal<String>();
    static void print(String str) {
        //打印当前线程中本地内存中本地变量的值
        System.out.println(str + " :" + localVar.get());
        //清除本地内存中的本地变量
        localVar.remove();
    }
    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            public void run() {
                ThreadLocaDemo.localVar.set("local_A");
                print("A");
                //打印本地变量
                System.out.println("after remove : " + localVar.get());
            }
        },"A").start();
        Thread.sleep(1000);
        new Thread(new Runnable() {
            public void run() {
                ThreadLocaDemo.localVar.set("local_B");
                print("B");
                System.out.println("after remove : " + localVar.get());
            }
        },"B").start();
    }
}

2.4ThrealLocal的内存泄漏问题

 public void set(T value) {
        //1、获取当前线程
        Thread t = Thread.currentThread();
        //2、获取线程中的属性 threadLocalMap ,如果threadLocalMap 不为空,
        //则直接更新要保存的变量值,否则创建threadLocalMap,并赋值
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            // 初始化thradLocalMap 并赋值
            createMap(t, value);
    }
  static class ThreadLocalMap {
        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
    }

可看出ThreadLocalMap是ThreadLocal的内部静态类,而它的构成主要是用Entry来保存数据 ,而且还是继承的弱引用.在Entry内部使用ThreadLocal作为key,使用我们设置的value作为value.

//这个是threadlocal 的内部方法
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    //ThreadLocalMap 构造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
    public T get() {
        //1、获取当前线程
        Thread t = Thread.currentThread();
        //2、获取当前线程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        //3、如果map数据为空,
        if (map != null) {
            //3.1、获取threalLocalMap中存储的值
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        //如果是数据为null,则初始化,初始化的结果,TheralLocalMap中存放key值为threadLocal,值为null
        return setInitialValue();
    }
private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
 public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

remove方法,直接将ThrealLocal 对应的值从当前相差Thread中的ThreadLocalMap中删除.为什么要删除,这涉及到内存泄露的问

实际上 ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,弱引用的特点是,如果这个对象只存在弱引用,那么在下一次垃圾回收的时候必然会被清理掉

所以如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候会被清理掉的,这样一来 ThreadLocalMap中使用这个 ThreadLocal 的 key 也会被清理掉。但是,value 是强引用,不会被清理,这样一来就会出现 key 为 null 的 value

ThreadLocal其实是与线程绑定的一个变量,如此就会出现一个问题:如果没有将ThreadLocal内的变量删除(remove)或替换,它的生命周期将会与线程共存。通常线程池中对线程管理都是采用线程复用的方法,在线程池中线程很难结束甚至于永远不会结束,这将意味着线程持续的时间将不可预测,甚至与JVM的生命周期一致。举个例字,如果ThreadLocal中直接或间接包装了集合类或复杂对象,每次在同一个ThreadLocal中取出对象后,再对内容做操作,那么内部的集合类和复杂对象所占用的空间可能会开始持续膨胀

在这里插入图片描述

在这里插入图片描述

我从这个图中我们可以非常直观的看出,ThreadLocalMap其实是Thread线程的一个属性值,而ThreadLocal是维护ThreadLocalMap这个属性指的一个工具类。

Thread线程可以拥有多个ThreadLocal维护的自己线程独享的共享变量(这个共享变量只是针对自己线程里面共享)

到此这篇关于Java中ThreadLocal的使用的文章就介绍到这了,更多相关ThreadLocal的使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java MyBatis框架环境搭建详解

    Java MyBatis框架环境搭建详解

    MyBatis本是apache的一个开源项目iBatis,MyBatis环境的搭建有点麻烦,本章带你了解搭建过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
    2022-08-08
  • java 非对称加密算法RSA实现详解

    java 非对称加密算法RSA实现详解

    这篇文章主要介绍了java 非对称加密算法RSA实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • 详解Spring batch 入门学习教程(附源码)

    详解Spring batch 入门学习教程(附源码)

    本篇文章主要介绍了Spring batch 入门学习教程(附源码),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • Elasticsearch写入瓶颈导致skywalking大盘空白

    Elasticsearch写入瓶颈导致skywalking大盘空白

    这篇文章主要为大家介绍了Elasticsearch写入瓶颈导致skywalking大盘空白的解决方案,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-02-02
  • Java是自学好还是参加培训班好?

    Java是自学好还是参加培训班好?

    这篇文章主要介绍了Java是自学好还是参加培训班好这一问题,给大家介绍了哪些人适合自学java,哪些人适合java培训班学习,大家可以看看内容详情
    2018-04-04
  • Spring Boot 调用外部接口的几种方式

    Spring Boot 调用外部接口的几种方式

    在微服务架构中,服务间的调用是不可或缺的环节,本文主要介绍了Spring Boot调用外部接口的几种方式,具有一定的参考价值,感兴趣的可以了解一下
    2024-05-05
  • SpringBoot返回json和xml的示例代码

    SpringBoot返回json和xml的示例代码

    本篇文章主要介绍了SpringBoot返回json和xml的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • 对Java的面对对象编程中对象和引用以及内部类的理解

    对Java的面对对象编程中对象和引用以及内部类的理解

    这篇文章主要介绍了对Java的面对对象编程中对象和引用以及内部类的理解,需要的朋友可以参考下
    2016-01-01
  • SpringCloud2020.0.x版UnderTow AccessLog相关配置简介

    SpringCloud2020.0.x版UnderTow AccessLog相关配置简介

    本文详细介绍了SpringCloud中AccessLog的相关配置,我们可以根据文中的相关数据配置出所需的AccessLog的信息以及格式,感兴趣的小伙伴可以参考一下
    2021-08-08
  • 聊聊springboot 整合 hbase的问题

    聊聊springboot 整合 hbase的问题

    这篇文章主要介绍了springboot 整合 hbase的问题,文中给大家提到配置linux服务器hosts及配置window hosts的相关知识,需要的朋友可以参考下
    2021-11-11

最新评论