【线程本地变量ThreadLocal】—— 每天一点小知识

简介: 【线程本地变量ThreadLocal】—— 每天一点小知识

🐳线程本地变量 ThreadLocal

ThreadLocal是Java中的一个重要概念,它为我们提供了一种在多线程环境下安全地共享数据的方式。在本篇文章中,我们将深入探讨ThreadLocal是什么、使用场景、具体用法以及其他相关知识点,从而帮助我们更好地理解和应用ThreadLocal。


一、ThreadLocal的概念

 💧ThreadLocal是Java中的一个类,它用于创建线程局部变量。线程局部变量是每个线程都有自己独立的一个变量副本,而这个副本对其他线程是不可见的。这意味着每个线程都可以修改自己的变量副本,而不会影响其他线程的变量副本。这种方式使得多线程下的数据共享变得相对安全。

 💧每一个 Thread 对象均含有一个 ThreadLocalMap 类型的成员变量 ThreadLocals,它存储本线程中所有ThreadLocal对象及其对应的值

 💧ThreadLocalMap 由一个个 Entry 对象构成。Entry 继承自 WeakReference<ThreadLocal<?>>一个 Entry 由 ThreadLocal 对象和 Object 构成(Object就是我们要存的值)。由此可见, Entry 的key是ThreadLocal对象,并且是一个弱引用。当没指向key的强引用后,该key就会被垃圾收集器回收。

 💧当执行set方法时,ThreadLocal首先会获取当前线程对象,然后获取当前线程的ThreadLocalMap对象。再以当前ThreadLocal对象为key,将值存储进ThreadLocalMap对象中。

 💧get方法执行过程类似。ThreadLocal首先会获取当前线程对象,然后获取当前线程的ThreadLocalMap对象。再以当前ThreadLocal对象为key,获取对应的value。

 💧由于每一条线程均含有各自私有的ThreadLocalMap容器,这些容器相互独立互不影响,因此不会存在线程安全性问题,从而也无需使用同步机制来保证多条线程访问容器的互斥性


二、ThreadLocal的使用场景

 💧ThreadLocal的主要使用场景总体包括以下几个方面:

  1. 线程数据共享在多线程环境下,如果多个线程需要共享一些数据,而又要避免数据竞争和不一致性问题,可以使用ThreadLocal来实现线程间的数据共享
  2. 线程安全性ThreadLocal变量在每个线程中都是独立的,因此可以用来保证线程安全性。例如,在Web应用中,可以使用ThreadLocal来存储当前线程的用户会话信息,以确保多个请求之间的用户会话信息隔离。
  3. 性能优化由于ThreadLocal变量在每个线程中都有自己的副本,因此可以避免线程之间共享数据带来的额外开销,从而提高程序的性能。

💧使用ThreadLocal来解决 数据库连接

private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
    public Connection initialValue() {
        return DriverManager.getConnection(DB_URL);
  }
};
public static Connection getConnection() {
  return connectionHolder.get();
}

💧使用ThreadLocal来解决 Session管理

private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
    Session s = (Session) threadSession.get();
    try {
        if (s == null) {
            s = getSessionFactory().openSession();
            threadSession.set(s);
        }
    } catch (HibernateException ex) {
        throw new InfrastructureException(ex);
    }
    return s;
}

三、ThreadLocal的具体用法

1. 创建ThreadLocal变量

 💧首先,我们需要创建一个ThreadLocal变量。例如:

public static final ThreadLocal<Integer> LOCAL_变量名 = new ThreadLocal<>();

2. 设置和获取ThreadLocal变量的值

 💧我们可以使用set方法来为每个线程设置一个独立的变量副本,如下所示:

LOCAL_变量名.set(值);

 💧我们可以通过get方法来获取当前线程的ThreadLocal变量值:

Object value = LOCAL_变量名.get();

3. 使用ThreadLocal作为方法局部变量

 💧我们可以在方法中使用ThreadLocal作为局部变量,这样每个线程都会有一个独立的ThreadLocal变量副本。例如:

public void someMethod() {
    LOCAL_变量名.set(值);
    // ... some code ...
    Object value = LOCAL_变量名.get();
    // ... some code ...
}

四、ThreadLocal其他知识点补充

1. ThreadLocal的原理和特点

 💧ThreadLocal的原理在于利用了Java的双重检查锁定(double-checked locking)机制。在每个线程中,当我们调用get方法时,会首先检查该线程是否已经有了该ThreadLocal变量的副本。如果没有,则进入同步块,再次检查其他线程是否已经创建了该副本。如果还没有,则创建该副本并返回。这种机制使得在多线程环境下,对ThreadLocal变量的访问和修改都能够保证线程安全。

2. ThreadLocal与NIO的关系

 💧NIO(New I/O)是Java提供的一种新的I/O操作方式,它支持非阻塞I/O操作。在NIO中,我们可以使用ThreadLocal来存储每个通道(Channel)的回调函数(Callback)。这样,每个通道都有自己的回调函数副本,从而避免了多个通道之间共享回调函数所带来的问题。

3. ThreadLocal的key

  • ThreadLocal的key是ThreadLocal对象本身。每个ThreadLocal对象内部都有一个Map,用于存储每个线程的变量副本,Map的key是ThreadLocal对象本身value是对应线程的变量副本
  • 在get方法中,先获取当前线程,然后从当前线程对应的Map中获取变量副本,如果不存在则通过initialValue方法初始化一个变量副本并存储到Map中。
  • 在set方法中,也是先获取当前线程,然后将变量副本存储到当前线程对应的Map中。
  • 在remove方法中,先获取当前线程,然后从当前线程对应的Map中移除变量副本。

五、使用ThreadLocal时的注意事项

使用ThreadLocal时需要注意以下几点↓

  1. 💧避免在ThreadLocal中使用过多变量副本,这会消耗额外的内存空间。

ThreadLocal是Java中的一个类,它用于创建线程局部变量。线程局部变量是每个线程都有自己独立的一个变量副本,而这个副本对其他线程是不可见的。这种方式使得多线程下的数据共享变得相对安全。但是,如果我们在ThreadLocal中存储过多的变量副本,就会消耗大量的内存空间。因此,在使用ThreadLocal时,应该尽可能地避免存储过多的变量副本,以避免消耗额外的内存空间。

  1. 💧在使用ThreadLocal时应该及时清理不再使用的变量副本,以避免内存泄漏问题。可以使用弱引用或者软引用来解决内存泄漏问题。

内存泄漏是一种常见的编程问题,它通常发生在长时间运行的程序中。内存泄漏是由于程序在申请内存后,无法释放未再使用的内存空间而导致的。如果内存泄漏持续发生,它将逐渐消耗可用内存,最终导致程序运行缓慢或崩溃。在使用ThreadLocal时,如果不及时清理不再使用的变量副本,就会导致内存泄漏问题的发生。为了解决这个问题,可以使用弱引用或者软引用来替代强引用,从而在对象不再需要时自动释放内存空间。

  1. 💧当不再需要ThreadLocal变量时,应该使用remove方法将其从当前线程的Map中移除,以释放内存空间

在ThreadLocal中,每个线程都有自己独立的变量副本,存储在Map中。当不再需要ThreadLocal变量时,应该及时将其从当前线程的Map中移除,以释放相应的内存空间。可以使用ThreadLocal的remove方法来实现这个操作。这样可以避免内存泄漏问题的发生,同时也可以提高程序的性能。

  1. 💧在使用InheritableThreadLocal时,需要注意变量副本的生命周期问题,避免子线程持有过期的变量副本。可以通过自定义ThreadLocal来实现。

InheritableThreadLocal是ThreadLocal的一个子类,它可以使得子线程继承父线程的变量副本。在使用InheritableThreadLocal时,需要注意变量副本的生命周期问题。如果父线程持有的变量副本已经过期,但是子线程仍然持有这个过期的变量副本,就会导致内存泄漏问题的发生。为了避免这个问题,可以通过自定义ThreadLocal来实现。在自定义的ThreadLocal中,可以维护一个存储变量副本的Map,并记录每个变量副本的生命周期。当变量副本过期时,可以将其从Map中移除。这样就可以避免子线程持有过期的变量副本的问题。

相关文章
|
2月前
|
Java 开发者
解锁并发编程新姿势!深度揭秘AQS独占锁&ReentrantLock重入锁奥秘,Condition条件变量让你玩转线程协作,秒变并发大神!
【8月更文挑战第4天】AQS是Java并发编程的核心框架,为锁和同步器提供基础结构。ReentrantLock基于AQS实现可重入互斥锁,比`synchronized`更灵活,支持可中断锁获取及超时控制。通过维护计数器实现锁的重入性。Condition接口允许ReentrantLock创建多个条件变量,支持细粒度线程协作,超越了传统`wait`/`notify`机制,助力开发者构建高效可靠的并发应用。
77 0
|
1月前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
|
1月前
|
存储 Ubuntu Linux
C语言 多线程编程(1) 初识线程和条件变量
本文档详细介绍了多线程的概念、相关命令及线程的操作方法。首先解释了线程的定义及其与进程的关系,接着对比了线程与进程的区别。随后介绍了如何在 Linux 系统中使用 `pidstat`、`top` 和 `ps` 命令查看线程信息。文档还探讨了多进程和多线程模式各自的优缺点及适用场景,并详细讲解了如何使用 POSIX 线程库创建、退出、等待和取消线程。此外,还介绍了线程分离的概念和方法,并提供了多个示例代码帮助理解。最后,深入探讨了线程间的通讯机制、互斥锁和条件变量的使用,通过具体示例展示了如何实现生产者与消费者的同步模型。
|
3月前
|
存储 SQL Java
(七)全面剖析Java并发编程之线程变量副本ThreadLocal原理分析
在之前的文章:彻底理解Java并发编程之Synchronized关键字实现原理剖析中我们曾初次谈到线程安全问题引发的"三要素":多线程、共享资源/临界资源、非原子性操作,简而言之:在同一时刻,多条线程同时对临界资源进行非原子性操作则有可能产生线程安全问题。
|
3月前
|
存储 Python 容器
Node中的AsyncLocalStorage 使用问题之在Python中,线程内变量的问题如何解决
Node中的AsyncLocalStorage 使用问题之在Python中,线程内变量的问题如何解决
|
3月前
|
存储 缓存 算法
同时使用线程本地变量以及对象缓存的问题
【7月更文挑战第15天】同时使用线程本地变量和对象缓存需小心处理以避免数据不一致、竞争条件及内存泄漏等问题。线程本地变量使各线程拥有独立存储,但若与对象缓存关联,可能导致多线程环境下访问旧数据。缺乏同步机制时,多线程并发修改缓存中的共享对象还会引起数据混乱。此外,若线程结束时未释放对象引用,可能导致内存泄漏。例如,在Web服务器场景下,若一更新缓存而另一线程仍获取旧数据,则可能返回错误信息;在图像处理应用中,若多线程无序修改算法对象则可能产生错误处理结果。因此,需确保数据一致性、避免竞争条件并妥善管理内存。
|
3月前
|
安全 Java
多线程线程安全问题之避免ThreadLocal的内存泄漏,如何解决
多线程线程安全问题之避免ThreadLocal的内存泄漏,如何解决
|
3月前
|
存储 安全 Java
多线程线程安全问题之ThreadLocal是什么,它通常用于什么场景
多线程线程安全问题之ThreadLocal是什么,它通常用于什么场景
|
3月前
|
安全 算法 Linux
【Linux】线程安全——补充|互斥、锁|同步、条件变量(下)
【Linux】线程安全——补充|互斥、锁|同步、条件变量(下)
43 0
|
3月前
|
存储 安全 Linux
【Linux】线程安全——补充|互斥、锁|同步、条件变量(上)
【Linux】线程安全——补充|互斥、锁|同步、条件变量(上)
51 0