一文彻底搞懂ThreadLocal

使用ThreadLocal存储用户登录信息 java代码解读复制代码= null?= null?= null?do { if (!} while(!= null?} }java代码解读复制代码} } }总结,就是在过滤器这边查询用户信息,存储到中java代码解读复制代码} }解释静态变量(static variable)是一种特殊的变量,它们属于类本身,而不是实例对象。静态变量是在类加载时初始化的,并且它们的生命周期是整个应用程序的生命周期。对于变量,情况略有不同。变量是线程局部变量,它们的生命周期是与线程相关的。 阅读详情

ThreadLocal

前言:在处理多线程并发安全的方法中,我们最常用的是通过锁来控制不同线程对临界区的访问,但即使加入乐观锁或悲观锁来控制多线程对于共享变量的控制,也会存在并发冲突,同时造成性能的影响。目前我们想彻底避免竞争,那threadLocal则是一个很好的选择了。

ThreadLocal是什么

定义:threadLocal顾名思义作为的是本地线程变量,其作用主要就是做数据隔离,保证在多线程环境下每个线程都独立包含一个变量,防止其他线程篡改。它提供了一种将可变数据通过每个线程有自己的独立副本从而实现线程封闭的机制。

private ThreadLocal<Integer> threadLocal = new ThreadLocal<>();	//创建一个ThreadLocal对象

ThreadLocal的作用

  • 线程并发: 在多线程并发的场景下

  • 传递数据: 我们可以通过ThreadLocal在同一线程,不同组件中传递公共变量

  • 线程隔离: 每个线程的变量都是独立的,不会互相影响

ThreadLocal的常用方法

方法声明描述
ThreadLocal()提供无参构造用于创建ThreadLocal对象
public void set( T value)设置当前线程的局部变量值
public T get()获取当前线程的局部变量值
public void remove()删除当前线程绑定的局部变量(避免内存泄漏)

ThreadLocal与synchronized

  • 多线程场景下无锁无本地线程变量:
public class ThreadLocalDemo1 {
    private String content;
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public static void main(String[] args) {
        ThreadLocalDemo1 threadLocalDemo1 = new ThreadLocalDemo1();
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(() -> {
                threadLocalDemo1.setContent(Thread.currentThread().getName() + "的数据");
                System.out.println(Thread.currentThread().getName() + "->" + threadLocalDemo1.getContent());
            });
            thread.setName("线程" + i);
            thread.start();
        }
    }
}
//输出结果(每次对应结果不相同,顺序不同)
线程0->线程2的数据
线程3->线程2的数据
线程2->线程2的数据
线程4->线程2的数据
线程1->线程2的数据
  • 多线程场景下使用synchronized关键字(以时间换空间)
public class ThreadLocalDemo2 {
    private String content;
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public static void main(String[] args) {
        ThreadLocalDemo2 threadLocalDemo2 = new ThreadLocalDemo2();
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(() -> {
                synchronized (ThreadLocalDemo2.class) {
                    threadLocalDemo2.setContent(Thread.currentThread().getName() + "的数据");
                    System.out.println(Thread.currentThread().getName() + "->" + threadLocalDemo2.getContent());
                }
            });
            thread.setName("线程" + i);
            thread.start();
        }
    }
}
//输出结果(通过synchronized关键字加锁,保证其代码串行执行。每次对应结果相同,顺序不同)
线程0->线程0的数据
线程4->线程4的数据
线程3->线程3的数据
线程2->线程2的数据
线程1->线程1的数据
  • 多线程场景下使用threadLocal(以空间换时间)
public class ThreadLocalDemo3 {
    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
    private String content;
    public String getContent() {
        return threadLocal.get();
    }
    public void setContent(String content) {
        threadLocal.set(content);
    }
    public static void main(String[] args) {
        ThreadLocalDemo3 threadLocalDemo3 = new ThreadLocalDemo3();
        for (int i = 0; i < 3; i++) {
            Thread thread = new Thread(() -> {
                threadLocalDemo3.setContent(Thread.currentThread().getName() + "的数据");
                System.out.println(Thread.currentThread().getName() + "->" + threadLocalDemo3.getContent());
            });
            thread.setName("线程" + i);
            thread.start();
        }
    }
}

//输出结果(通过threadLocal定义本地线程变量,在多线程场景下,每个线程都包含一个threadLocal,实现线程隔离。每次对应结果相同,顺序不同)
线程1->线程1的数据
线程0->线程0的数据
线程2->线程2的数据

ThreadLocal的应用场景

  • Spring实现事务隔离级别源码,Spring通过ThreadLocal来保证单个线程中的数据库操作是同一个数据库连接,使业务层无感知并管理connection对象,通过传播级别来管理多线程时事务之间的切换、挂起、恢复。
//代码操作数据库需要getConnection()获取连接并在执行完响应操作之后需要关闭连接,而在多线程场景下,每个线程都能获取连接,且可能多个线程获取的连接是相同的,此时就会造成两个线程同用一个连接且其中一个线程使用完之后执行connection.close(),而另一个线程无法完成后续操作导致报错。
  • 用户登录信息存储,多线程环境下保存不同用户登录信息,在业务层次复杂的情况下,我们无需通过获取用户信息后一层一层传到底层,可直接通过threadLocal获取存储的用户信息。
//很多场景下我们需要统一存入用户信息,当某一个逻辑需要用户信息时,我们无需通过参数传入的方式直接通过ThreadLocal里的get()拿到用户信息,减少代码的强耦合性。

ThreadLocal内部结构

JDK8之前

在这里插入图片描述

​ 每个ThreadLocal都保存了一个ThreadLocalMap,Map里面保存着有个静态内部类Entry作为节点,形成一个Entry数组,数组中元素Entry的key为当前线程,value为保存的值。

JDK8及之后

在这里插入图片描述

​ 每个Thread都保存了一个ThreadLocalMap,ThreadLocalMap(并非传统意义上的Map,不属于java.util.Map,只是跟传统Map实现方式很像)里面保存着有个静态内部类Entry作为节点的数组,数组中元素Entry的key为ThreadLocal实例本身,value为保存的值。

在这里插入图片描述

好处:

  • 每个Map存储的Entry数量变少,因为之前的存储数量由Thread的数量决定,现在是由ThreadLocal的数量决定。在实际运用当中,往往ThreadLocal的数量要少于Thread的数量。
  • 当Thread销毁时,ThreadLocalMap也会随之销毁,能减少内存的使用。

ThreadLocal方法解析

方法声明描述
public void set( T value)设置当前线程绑定的局部变量
public T get()获取当前线程绑定的局部变量
public void remove()移除当前线程绑定的局部变量
protected T initialValue()返回当前线程局部变量的初始值(未重写则直接返回null,需通过子类重写设置初始值)
set方法
  • 执行流程
  1. 首先获取当前线程,并根据当前线程获取一个Map

  2. 如果获取的Map不为空,则将参数设置到Map中(当前ThreadLocal的引用作为key)

  3. 如果Map为空,则给该线程创建 Map,并设置初始值

  • 代码展示
/**
     * 设置当前线程对应的ThreadLocal的值
     *
     * @param value 将要保存在当前线程对应的ThreadLocal的值
     */
    public void set(T value) {
        // 获取当前线程对象
        Thread t = Thread.currentThread();
        // 获取此线程对象中维护的ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        // 判断map是否存在
        if (map != null)
            // 存在则调用map.set设置此实体entry
            map.set(this, value);
        else
            // 1)当前线程Thread 不存在ThreadLocalMap对象
            // 2)则调用createMap进行ThreadLocalMap对象的初始化
            // 3)并将 t(当前线程)和value(t对应的值)作为第一个entry存放至ThreadLocalMap中
            createMap(t, value);
    }

 /**
     * 获取当前线程Thread对应维护的ThreadLocalMap 
     * 
     * @param  t the current thread 当前线程
     * @return the map 对应维护的ThreadLocalMap 
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
	/**
     *创建当前线程Thread对应维护的ThreadLocalMap 
     *
     * @param t 当前线程
     * @param firstValue 存放到map中第一个entry的值
     */
	void createMap(Thread t, T firstValue) {
        //这里的this是调用此方法的threadLocal
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
get方法
  • 执行过程
  1. 首先获取当前线程, 根据当前线程获取一个Map

  2. 如果获取的Map不为空,则在Map中以ThreadLocal的引用作为key来在Map中获取对应的Entry e,否则转到步骤4

  3. 如果e不为null,则返回e.value,否则转到步骤4

  4. Map为空或者e为空,则通过initialValue函数获取初始值value,然后用ThreadLocal的引用和value作为firstKey和firstValue创建一个新的Map

总结: 先获取当前线程的 ThreadLocalMap 变量,如果存在则返回值,不存在则创建并返回初始值(不设置即返回null)。

  • 代码展示
/**
 * 返回当前线程中保存ThreadLocal的值
 * 如果当前线程没有此ThreadLocal变量,
 * 则它会通过调用{@link #initialValue} 方法进行初始化值
 *
 * @return 返回当前线程对应此ThreadLocal的值
 */
public T get() {
    // 获取当前线程对象
    Thread t = Thread.currentThread();
    // 获取此线程对象中维护的ThreadLocalMap对象
    ThreadLocalMap map = getMap(t);
    // 如果此map存在
    if (map != null) {
        // 以当前的ThreadLocal 为 key,调用getEntry获取对应的存储实体e
        ThreadLocalMap.Entry e = map.getEntry(this);
        // 对e进行判空 
        if (e != null) {
            @SuppressWarnings("unchecked")
            // 获取存储实体 e 对应的 value值
            // 即为我们想要的当前线程对应此ThreadLocal的值
            T result = (T)e.value;
            return result;
        }
    }
    /*
      初始化 : 有两种情况有执行当前代码
      第一种情况: map不存在,表示此线程没有维护的ThreadLocalMap对象
      第二种情况: map存在, 但是没有与当前ThreadLocal关联的entry
     */
    return setInitialValue();
}

/**
 * 初始化
 *
 * @return the initial value 初始化后的值
 */
private T setInitialValue() {
    // 调用initialValue获取初始化的值
    // 此方法可以被子类重写, 如果不重写默认返回null
    T value = initialValue();
    // 获取当前线程对象
    Thread t = Thread.currentThread();
    // 获取此线程对象中维护的ThreadLocalMap对象
    ThreadLocalMap map = getMap(t);
    // 判断map是否存在
    if (map != null)
        // 存在则调用map.set设置此实体entry
        map.set(this, value);
    else
        // 1)当前线程Thread 不存在ThreadLocalMap对象
        // 2)则调用createMap进行ThreadLocalMap对象的初始化
        // 3)并将 t(当前线程)和value(t对应的值)作为第一个entry存放至ThreadLocalMap中
        createMap(t, value);
    // 返回设置的值value
    return value;
}
remove方法
  • 执行过程
  1. 首先获取当前线程,并根据当前线程获取一个Map

  2. 如果获取的Map不为空,则移除当前ThreadLocal对象对应的entry

  • 代码展示
/**
 * 删除当前线程中保存的ThreadLocal对应的实体entry
 */
public void remove() {
    // 获取当前线程对象中维护的ThreadLocalMap对象
    ThreadLocalMap m = getMap(Thread.currentThread());
    // 如果此map存在
    if (m != null)
    // 存在则调用map.remove
    // 以当前ThreadLocal为key删除对应的实体entry
    m.remove(this);
}
initValue方法
  • 执行过程

此方法的作用是 返回该线程局部变量的初始值。

(1) 这个方法是一个延迟调用方法,从上面的代码我们得知,在set方法还未调用而先调用了get方法时才执行,并且仅执行1次。

(2)这个方法缺省实现直接返回一个null。

(3)如果想要一个除null之外的初始值,可以重写此方法。(备注: 该方法是一个protected的方法,显然是为了让子类覆盖而设计的)

  • 代码展示
/**
  * 返回当前线程对应的ThreadLocal的初始值
  * 此方法的第一次调用发生在,当线程通过get方法访问此线程的ThreadLocal值时
  * 除非线程先调用了set方法,在这种情况下,initialValue 才不会被这个线程调用。
  * 通常情况下,每个线程最多调用一次这个方法。
  *
  * <p>这个方法仅仅简单的返回null {@code null};
  * 如果程序员想ThreadLocal线程局部变量有一个除null以外的初始值,
  * 必须通过子类继承{@code ThreadLocal} 的方式去重写此方法
  * 通常, 可以通过匿名内部类的方式实现
  *
  * @return 当前ThreadLocal的初始值
  */
protected T initialValue() {
    return null;
}

代码解析

​ ThreadLocalMap属于ThreadLocal的静态内部类,Entry是ThreadLocalMap的静态内部类,其中ThreadLocalMap保存着Entry的数组,Entry中限定了ThreadLocal作为key以及设置传入参数值为value。Entry继承WeakReference,也就是key(ThreadLocal)是弱引用(弱引用将在下一次GC时回收),其目的是将ThreadLocal对象的生命周期和线程生命周期解绑。

public class ThreadLocal<T> {
    private final int threadLocalHashCode = nextHashCode();
    private static AtomicInteger nextHashCode = new AtomicInteger();
    private static final int HASH_INCREMENT = 1640531527;
    ...
    static class ThreadLocalMap {
        private static final int INITIAL_CAPACITY = 16;
        private ThreadLocal.ThreadLocalMap.Entry[] table;
        private int size = 0;
        private int threshold;

        private ThreadLocalMap(ThreadLocal.ThreadLocalMap parentMap) {
            ThreadLocal.ThreadLocalMap.Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            this.setThreshold(len);
            this.table = new ThreadLocal.ThreadLocalMap.Entry[len];
            ThreadLocal.ThreadLocalMap.Entry[] var4 = parentTable;
            int var5 = parentTable.length;
            ...
        }
        ...
        static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                this.value = v;
            }
        }
        ...
    }
}

弱引用和内存泄漏

弱引用和强引用
概念

​ 弱引用:具有弱引用的对象拥有更短暂的生命周期,在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

​ 强引用:我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾回收器就不会回收这种对象。

内存泄露
概念
  • Memory overflow:内存溢出,没有足够的内存提供申请者使用。
  • Memory leak: 内存泄漏是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。内存泄漏的堆积终将导致内存溢出。
设想
如果key使用强引用

​ 假设在业务代码中使用完ThreadLocal ,threadLocal Ref被回收了。但是因为threadLocalMap的Entry强引用了threadLocal,造成threadLocal无法被回收。在没有手动删除这个Entry以及CurrentThread依然运行的前提下,始终有强引用链 threadRef->currentThread->threadLocalMap->entry,Entry就不会被回收(Entry中包括了ThreadLocal实例和value),导致Entry内存泄漏。也就是说,ThreadLocalMap中的key使用了强引用, 是无法完全避免内存泄漏的。

在这里插入图片描述

如果key使用弱引用

​ 同样假设在业务代码中使用完ThreadLocal ,threadLocal Ref被回收了。由于ThreadLocalMap只持有ThreadLocal的弱引用,没有任何强引用指向threadlocal实例, 所以threadlocal就可以顺利被gc回收,此时Entry中的key=null。但是在没有手动删除这个Entry以及CurrentThread依然运行的前提下,也存在有强引用链 threadRef->currentThread->threadLocalMap->entry -> value ,value不会被回收, 而这块value永远不会被访问到了,导致value内存泄漏。也就是说,ThreadLocalMap中的key使用了弱引用, 也有可能内存泄漏。

在这里插入图片描述

问题

​ 目前针对于ThreadLocal中ThreadLocalMap下的Entry就是继承了弱引用,发生GC时ThreadLocal实例会被回收,也就是key将为null,如果创建ThreadLocal的线程一直持续运行,那么这个Entry对象中的value就有可能一直得不到回收,发生内存泄露。就比如线程池里面的线程,线程都是复用的,那么之前的线程实例处理完之后,出于复用的目的线程依然存活,所以,ThreadLocal设定的value值被持有,导致内存泄露。

在这里插入图片描述

如何解决

​ 内存泄漏的发生跟ThreadLocalMap中的key是否使用弱引用是没有关系的,无论是强引用还是弱引用都存在内存泄漏,问题出现的前提是没有手动删除这个Entry或者当前线程依旧在运行。解决办法如下:

  • 当使用完ThreadLocal后手动进行remove操作,将当前ThreadLocal的Entry直接删除。
  • 存在内存泄露是因为当前key为null,无法再引用而使其生命周期与当前线程生命周期相同,只要当前线程运行结束,内存泄漏也即将不复存在

总结:第二种方法相对于第一种方法更加不容易控制,尤其是使用线程池的时候,线程是可复用的,也就表示即使使用完后线程依旧存活,内存泄漏也就一直存在。

疑问:

那为什么强引用和弱引用都很难避免内存泄漏,但设计却选用了弱引用?

事实上,在ThreadLocalMap中的set/getEntry方法中,会对key为null(也就是ThreadLocal为null)进行判断,如果为null的话,那么是会对value置为null的。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

​ 这就意味着使用完ThreadLocal,CurrentThread依然运行的前提下,就算忘记调用remove方法,弱引用比强引用可以多一层保障:弱引用的ThreadLocal会被回收,对应的value在下一次ThreadLocalMap调用set、get、remove中的任一方法的时候会被清除,从而避免内存泄漏。

hash冲突

什么是hash冲突

​ hash即为散列,hash冲突就是key通过hash算法最终得到的值作为地址存放到当前的键值对(key-value)上,但发现此地址已存有其他的对象,从而引发的冲突。在hashMap中,我们都知道是由数组加链表组成(JDK8后数组+链表+红黑树),其中链表就是为了解决hash冲突的。

问题

​ 由上所知ThreadLocal中维护着一个ThreadLocalMap,ThreadLocalMap不同于HashMap,但实现方式非常相似。但ThreadLocalMap中只包含了Entry数组,为什么只用数组却根本没有链表的形式,这又怎么解决hash冲突这个问题呢?

为什么使用数组

​ 我们开发过程中单个线程是可能会包含多个ThreadLocal的,通过多个ThreadLocal来保存多个对象,ThreadLocalMap保存的正是当前多个ThreadLocal实例的threadLocalHashCode作为key的Entry节点。

怎么解决Hash冲突

源码展示

在这里插入图片描述

所有ThreadLocal进行set值的时候都会通过key.threadLocalHashCode & len - 1算出在Entry数组中所存放的位置,然后根据如下情况赋值:

  • 如果当前位置为空,就初始化一个Entry对象并存入当前set的值。
  • 如果当前位置不为空,并且key和当前Entry的key相同的话,则直接更新value值。
  • 如果当前位置不为空,并且key和当前Entry的key不相同的话,则找下一个空位置,以此类推,直到找到空位置为止。

总结:以上所知,虽然ThreadLocal不存在链表形式,该方法一次探测下一个地址,直到有空的地址后插入,若整个空间都找不到空余的地址,则产生溢出。ThreadLocalMap使用线性探测法来解决哈希冲突的。

举个例子,假设当前table长度为16,也就是说如果计算出来key的hash值为14,如果table[14]上已经有值,并且其key与当前key不一致,那么就发生了hash冲突,这个时候将14加1得到15,取table[15]进行判断,这个时候如果还是冲突会回到0,取table[0],以此类推,直到可以插入。

eadLocal进行set值的时候都会通过key.threadLocalHashCode & len - 1算出在Entry数组中所存放的位置,然后根据如下情况赋值:

  • 如果当前位置为空,就初始化一个Entry对象并存入当前set的值。
  • 如果当前位置不为空,并且key和当前Entry的key相同的话,则直接更新value值。
  • 如果当前位置不为空,并且key和当前Entry的key不相同的话,则找下一个空位置,以此类推,直到找到空位置为止。

总结:以上所知,虽然ThreadLocal不存在链表形式,该方法一次探测下一个地址,直到有空的地址后插入,若整个空间都找不到空余的地址,则产生溢出。ThreadLocalMap使用线性探测法来解决哈希冲突的。

举个例子,假设当前table长度为16,也就是说如果计算出来key的hash值为14,如果table[14]上已经有值,并且其key与当前key不一致,那么就发生了hash冲突,这个时候将14加1得到15,取table[15]进行判断,这个时候如果还是冲突会回到0,取table[0],以此类推,直到可以插入。

按照上面的描述,可以把Entry[] table看成一个环形数组。

详解ThreadLocal和其中的应用场景 好了,本篇ThreadLocal的相关知识就分享到这里了。 阅读详情

相关推荐

ThreadLocal万字图文全面解析!一篇文章彻底搞懂ThreadLocal

真的没有那么简单的ThreadLocal: ThreadLocal的key是弱引用,那么在 threadLocal.get()的时候,发生GC之后,key是否为null? ThreadLocal中ThreadLocalMap 的数据结构? ThreadLocalMap 的Hash算法? ThreadLocalMap 中Hash冲突 如何解决? ThreadLocalMap扩容机制? Thread...

GitChat 1933

ThreadLocal保存用户信息

ThreadLocal

qq_42582773的博客 3109

COLMAP在Ubuntu下的编译陷阱与实战避坑指南——从环境配置到三维重建全流程

本文详细介绍了在Ubuntu系统下编译和配置COLMAP三维重建工具的全流程,包括环境准备、依赖库安装、CUDA加速配置以及常见编译问题的解决方案。通过实战案例演示了从图像特征提取到稠密重建的完整步骤,并提供了性能优化和问题排查的实用技巧,帮助用户高效完成三维重建任务。

weixin_29003437的博客 1048

一文搞懂 ThreadLocal,是时候反问面试官了

ThreadLocaL是Java中的一个线程级别的变量,它提供了一种将数据与每个线程关联起来的机制。每个线程都有自己独立的实例,可以在这个实例中存储和获取数据,而不会与其他线程的数据产生冲突。的作用和用途主要有以下几个方面:可以用于保存每个线程所需的私有数据。例如,在多线程环境下,如果有一个对象需要在线程之间共享,但又希望每个线程都拥有它的私有拷贝,则可以使用来存储这个对象。这样,每个线程都可以独立地读取和修改自己的私有拷贝,而互不干扰。可以避免使用线程同步机制(如锁)来保护共享数据,从而提高程序的并发性能

兴趣是最好的老师,勤能补拙 7668

吃透Chisel语言.02.Chisel+VS Code+Ubuntu/WSL2开发环境搭建与使用

既然是学Chisel语言,那肯定得先把环境配置好,磨刀不误砍柴工嘛。而且有个测试环境的话,在后期的学习过程中自己去试运行代码段可以加深学习效果。这里推荐使用Ubuntu或者基于WSL2(Windows Subsystem Linux 2)的Ubuntu,本人使用的是WSL2上的Ubuntu 20.04.3 LTS,在使用体验上与Ubuntu几乎没有区别,且能同时使用Windows提供的诸多便利。...

github-3rr0r的博客 8448

从零到一:用AXI DataMover在Zynq MPSoC上实现高效DMA传输(附Vitis HLS驱动示例)

本文详细介绍了如何在Xilinx Zynq UltraScale+ MPSoC平台上使用AXI DataMover实现高效的DMA数据传输。通过解析AXI DataMover架构、Vivado工程配置、Vitis HLS驱动开发及性能优化技巧,帮助开发者快速构建高性能数据传输通道,并提供了可直接复用的代码示例。

weixin_42520796的博客 425

理解ThreadLocal

理解ThreadLocal 1.概览: ThreadLocal提供了线程的局部变量,每个线程都可以通过 set() 和 get() 来对这个局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离。简单的来说往 ThreadLocal 中填充的变量是属于当前线程的。设计的目的就是为了能够在当前线程中有属于自己的变量,并不是为了解决并发或者共享变量的问题 2.重要属性: // threadLocalHashCode 表示当前 ThreadLocal 的 hashCode,用于计算当前 T

Cherish@WVv 543

ThinkPHP+Layui开发的ERP管理系统

ERP采购生产销售系统,一款基于ThinkPHP+Layui开发的ERP管理系统,帮助中小企业实现ERP管理规范化,此系统能为你解决五大方面的经营问题:1.采购管理 2.销售管理 3.仓库管理 4.资金管理 5.生产管理,适用于:服装鞋帽、化妆品、机械机电、家具装潢、建材行业、快销品、母婴用品、办公用品、汽配汽修、生鲜农贸、手机通讯、数码家电、医药用品、制造业、信息技术服务行业等。提供全部无加密服务端和前端源代码,独立部署,不受限制。

u011092458的博客 788

一文带你彻底搞懂ThreadLocal

ThreadLocal的相关问题也是比较常见的面试题了,网上也有相当多的文章,但是看了很多篇,也没说实际场景,内容也都差不多。ThreadLocalMap、弱引用、内存泄漏等概念都能背下来了,但还是不能理解,真就是当八股文在背。所以自己去看源码捋了一下终于搞懂了,真是说一千道一万,不如源码看一看。

XUchenmp的博客 718

基于Android13的系统启动流程分析(三)之FirstStageMain阶段

init进程是Android系统中用户空间的第一个进程,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建zygote(孵化器)和属性服务等。init进程是由多个源文件共同组成的,这些文件位于源码目录system/core/init1. Bootloader 引导当按下设备电源键时,最先运行的就是 bootloader(固化在ROM的程序),bootloader 的主要作用就是硬件设备(如 CPU、flash、内存)的初始化并加载到RAM,通过建立内存空间映射,为装载 Linux 内核做好准备,。

q1210249579的博客 5496

Conda换源全攻略:从清华源到默认源的保姆级切换教程(附常见问题解决)

本文提供Conda换源的完整教程,详细讲解如何从清华源切换到默认源,包括配置步骤、常见问题解决方案及高级技巧。针对国内开发者遇到的下载速度慢问题,清华源提供了高速稳定的解决方案,同时文章也介绍了恢复默认源的方法和多种镜像源选择。

weixin_28837817的博客 940

threadlocal用法_一文带你彻底搞懂ThreadLocal

前言共享变量一直是并发中的老大难问题,每个线程都对它有操作权,所以线程之间的同步很关键,锁也就应运而生。这里换一个思路,是否可以把共享变量私有化?即每个线程都拥有一份共享变量的本地副本,每个线程对应一个副本,同时对共享变量的操作也改为对属于自己的副本的操作,这样每个线程处理自己的本地变量,形成数据隔离。事实上这就是ThreadLocal了。就线程同步而言,锁可以认为是时间换空间,ThreadLoc...

weixin_39864571的博客 259

手把手教你学Simulink——基础电机控制场景实例:基于Simulink的永磁同步发电机温度场耦合仿真

摘要:本文详细介绍了基于Simulink的永磁同步发电机(PMSG)电-热耦合仿真方法。通过Simscape Electrical搭建含热端口的PMSG模型,结合集总参数热网络(LPTN)实现损耗计算与温度反馈,分析了过载工况下温升对电阻和永磁体性能的影响。仿真结果表明:150%过载20秒会导致绕组温升132°C,电阻增加42%,磁链衰减10.5%。文章还提供了自然冷却与强迫风冷的对比数据,并建议通过多物理场耦合仿真预防实机过热问题。该方法可有效支撑高功率密度电机热设计,避免永磁体退磁风险。

MHD0815的博客 251

【高并发】多线程环境下如何保证共享变量的安全性?冰河建议你使用ThreadLocal,一文带你彻底搞懂ThreadLocal

ThreadLocal是JDK提供的,支持线程本地变量。也就是说,如果我们创建了一个ThreadLocal变量,则访问这个变量的每个线程都会有这个变量的一个本地副本。如果多个线程同时对这个变量进行读写操作时,实际上操作的是线程自己本地内存中的变量,从而避免了线程安全的问题。

冰河的专栏 3878

​​手把手教你学PCIE--ATU 的 Bar 匹配模式与地址匹配模式详解​

摘要: ATU(地址转换单元)提供两种地址匹配模式:Bar匹配模式基于PCI设备的BAR寄存器自动映射虚拟地址(只需配置物理基址),适用于标准PCI设备DMA;地址匹配模式需显式配置虚拟与物理地址范围,适合自定义映射场景。核心区别在于匹配依据(BAR自动推导vs人工定义),Bar模式简化配置,地址模式更灵活。

MHD0815的博客 380

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

一文搞懂 ThreadLocal 原理

当多线程访问共享可变数据时,涉及到线程间同步的问题,并不是所有时候,都要用到共享数据,所以就需要线程封闭出场了。 数据都被封闭在各自的线程之中,就不需要同步,这种通过将数据封闭在线程中而避免使用同步的技术称为线程封闭。 本文主要介绍线程封闭中的其中一种体现:ThreadLocal,将会介绍什么是 ThreadLocal;从 ThreadLocal 源码角度分析,最后介绍 ThreadLocal 的...

武培轩 2358

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

一文搞懂 ThreadLocal

ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制。ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用。提供一个线程内公共变量(比如本次请求的用户信息),减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度,或者为线程提供一个私有的变量副本,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

程序员子龙 1197

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

【源码阅读】一文搞懂 ThreadLocal 原理以及使用场景

无论是工作还是面试,我们都会跟打交道,今天就跟大家聊聊哈~ThreadLocal是什么?为什么要使用ThreadLocal一个ThreadLocal的使用案例ThreadLocal的原理为什么不直接用线程id作为ThreadLocalMap的key为什么会导致内存泄漏呢?是因为弱引用吗?Key为什么要设计成弱引用呢?强引用不行?InheritableThreadLocal保证父子线程间的共享数据ThreadLocal的应用场景和使用注意点。

欢迎来到 程序猿 的博客 6564

一文让你彻底掌握ThreadLocal

对共享变量加锁虽然能够保证线程的安全,但是却增加了开发人员对锁的使用技能,如果锁使用不当,则会导致死锁的问题。而ThreadLocal能够做到在创建变量后,每个线程对变量访问时访问的是线程自己的本地变量。

华为云官方博客 1364

科普:一文搞懂ThreadLocal

ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。

为无为,事无事,味无味。 1576

threadlocal 要不要static_一文搞懂 ThreadLocal 原理

当多线程访问共享可变数据时,涉及到线程间同步的问题,并不是所有时候,都要用到共享数据,所以就需要线程封闭出场了。数据都被封闭在各自的线程之中,就不需要同步,这种通过将数据封闭在线程中而避免使用同步的技术称为线程封闭。本文主要介绍线程封闭中的其中一种体现:ThreadLocal,将会介绍什么是 ThreadLocal;从 ThreadLocal 源码角度分析,最后介绍 ThreadLocal 的应用...

weixin_39571938的博客 1435

彻底搞懂ThreadLocal

ThreadLocal的原理、线程隔离、线程安全、内存泄漏、弱连接

阿果子的博客 636
上一篇: 白嫖之路—内网穿透
下一篇: mybatis批量新增返回主键问题
人生苦短,及时编码
博客等级 码龄5年 3粉丝 13原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值