线程本地变量 T h r e a d L o c a l \color{#FF1493}{线程本地变量ThreadLocal} 线程本地变量ThreadLocal
仰望天空,妳我亦是行人.✨
个人主页——微风撞见云的博客
《数据结构与算法》专栏的文章图文并茂生动形象简单易学!欢迎大家来踩踩~
《Java学习笔记》专栏的文章是本人在Java学习中总结的一些知识点~
《每天一点小知识》专栏的文章可以丰富你的知识库,滴水成河~
《Redis》专栏的文章是在学习Redis时,整理的笔记与记录的思考~
《RabbitMQ》专栏的文章是在学习尚硅谷课程时整理的笔记,方便复习巩固~
希望本文能够给读者带来一定的帮助~文章粗浅,敬请批评指正!
ThreadLocal是Java中的一个重要概念,它为我们提供了一种在多线程环境下安全地共享数据的方式。在本篇文章中,我们将深入探讨ThreadLocal是什么、使用场景、具体用法以及其他相关知识点,从而帮助我们更好地理解和应用ThreadLocal。
ThreadLocal是Java中的一个类,它用于创建线程局部变量。线程局部变量是每个线程都有自己独立的一个变量副本,而这个副本对其他线程是不可见的。这意味着每个线程都可以修改自己的变量副本,而不会影响其他线程的变量副本
。这种方式使得多线程下的数据共享变得相对安全。
每一个 Thread 对象均含有一个 ThreadLocalMap
类型的成员变量 ThreadLocals
,它存储本线程中所有ThreadLocal对象及其对应的值
。
ThreadLocalMap 由一个个 Entry
对象构成。Entry 继承自 WeakReference
,一个 Entry 由 ThreadLocal 对象和 Object 构成
(Object就是我们要存的值)。由此可见, Entry 的key是ThreadLocal对象,并且是一个弱引用
。当没指向key的强引用后,该key就会被垃圾收集器回收。
当执行set方法时,ThreadLocal首先会获取当前线程对象,然后获取当前线程的ThreadLocalMap对象。再以当前ThreadLocal对象为key
,将值存储进ThreadLocalMap对象中。
get方法执行过程类似。ThreadLocal首先会获取当前线程对象,然后获取当前线程的ThreadLocalMap对象。再以当前ThreadLocal对象为key,获取对应的value。
由于每一条线程均含有各自私有的ThreadLocalMap容器,这些容器相互独立互不影响,因此不会存在线程安全性问题,从而也无需使用同步机制来保证多条线程访问容器的互斥性
。
ThreadLocal的主要使用场景总体包括以下几个方面:
线程数据共享
:在多线程环境下,如果多个线程需要共享一些数据,而又要避免数据竞争和不一致性问题,可以使用ThreadLocal来实现线程间的数据共享
。线程安全性
:ThreadLocal变量在每个线程中都是独立的,因此可以用来保证线程安全性。例如,在Web应用中,可以使用ThreadLocal来存储当前线程的用户会话信息
,以确保多个请求之间的用户会话信息隔离。性能优化
:由于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变量。例如:
public static final ThreadLocal<Integer> LOCAL_变量名 = new ThreadLocal<>();
我们可以使用set
方法来为每个线程设置一个独立的变量副本,如下所示:
LOCAL_变量名.set(值);
我们可以通过get
方法来获取当前线程的ThreadLocal变量值:
Object value = LOCAL_变量名.get();
我们可以在方法中使用ThreadLocal作为局部变量,这样每个线程都会有一个独立的ThreadLocal变量副本。例如:
public void someMethod() {
LOCAL_变量名.set(值);
// ... some code ...
Object value = LOCAL_变量名.get();
// ... some code ...
}
ThreadLocal的原理在于利用了Java的双重检查锁定(double-checked locking)机制。在每个线程中,当我们调用get
方法时,会首先检查该线程是否已经有了该ThreadLocal变量的副本。如果没有,则进入同步块,再次检查其他线程是否已经创建了该副本。如果还没有,则创建该副本并返回。这种机制使得在多线程环境下,对ThreadLocal变量的访问和修改都能够保证线程安全。
NIO(New I/O)是Java提供的一种新的I/O操作方式,它支持非阻塞I/O操作。在NIO中,我们可以使用ThreadLocal
来存储每个通道(Channel)的回调函数(Callback)。这样,每个通道都有自己的回调函数副本,从而避免了多个通道之间共享回调函数所带来的问题。
ThreadLocal的key是ThreadLocal对象本身。每个ThreadLocal对象内部都有一个Map
,用于存储每个线程的变量副本,Map的key是ThreadLocal对象本身
,value是对应线程的变量副本
。
在get方法中,先获取当前线程,然后从当前线程对应的Map中获取变量副本,如果不存在则通过initialValue方法初始化一个变量副本并存储到Map中。
在set方法中,也是先获取当前线程,然后将变量副本存储到当前线程对应的Map中。
在remove方法中,先获取当前线程,然后从当前线程对应的Map中移除变量副本。
使用ThreadLocal时需要注意以下几点↓
过多
变量副本,这会消耗额外的内存空间。ThreadLocal是Java中的一个类,它用于创建线程局部变量。线程局部变量是每个线程都有自己独立的一个变量副本,而这个副本对其他线程是不可见的。这种方式使得多线程下的数据共享变得相对安全。但是,如果我们在ThreadLocal中存储过多的变量副本,就会消耗大量的内存空间。因此,在使用ThreadLocal时,应该尽可能地避免存储过多的变量副本,以避免消耗额外的内存空间。
及时清理不再使用的变量副本
,以避免内存泄漏
问题。可以使用弱引用或者软引用来解决内存泄漏问题。内存泄漏是一种常见的编程问题,它通常发生在长时间运行的程序中。内存泄漏是由于程序在申请内存后,无法释放未再使用的内存空间而导致的。如果内存泄漏持续发生,它将逐渐消耗可用内存,最终导致程序运行缓慢或崩溃。在使用ThreadLocal时,如果不及时清理不再使用的变量副本,就会导致内存泄漏问题的发生。为了解决这个问题,可以使用弱引用或者软引用来替代强引用,从而在对象不再需要时自动释放内存空间。
remove
方法将其从当前线程的Map中移除,以释放内存空间
。在ThreadLocal中,每个线程都有自己独立的变量副本,存储在Map中。当不再需要ThreadLocal变量时,应该及时将其从当前线程的Map中移除,以释放相应的内存空间。可以使用ThreadLocal的remove方法来实现这个操作。这样可以避免内存泄漏问题的发生,同时也可以提高程序的性能。
InheritableThreadLocal
时,需要注意变量副本的生命周期
问题,避免子线程持有过期的变量副本
。可以通过自定义ThreadLocal来实现。InheritableThreadLocal是ThreadLocal的一个子类,它可以使得子线程继承父线程的变量副本。在使用InheritableThreadLocal时,需要注意变量副本的生命周期问题。如果父线程持有的变量副本已经过期,但是子线程仍然持有这个过期的变量副本,就会导致内存泄漏问题的发生。为了避免这个问题,可以通过自定义ThreadLocal来实现。在自定义的ThreadLocal中,可以维护一个存储变量副本的Map,并记录每个变量副本的生命周期。当变量副本过期时,可以将其从Map中移除。这样就可以避免子线程持有过期的变量副本的问题。
初学一门技术时,总有些许的疑惑,别怕,它们是我们学习路上的点点繁星,帮助我们不断成长。
积少成多,滴水成河。文章粗浅,希望对大家有帮助!
1024 程序员节快乐~