在谈ThreadLocal之前,先简单说一下一致性问题及其解决方法。
1.一致性问题
发生在多个主体对同一份数据无法达成共识。包括分布式一致性问题、并发问题等。特点是场景多、问题复杂、难以察觉——需要严密的思考甚至数学论证。
2.一致性问题解决方法
一致性问题的解决方法通常有3个。第一排队:比如,锁、互斥量、管程、屏障等。第二投票:例如,paxos、Raft等。这两种都会产生额外的开销。还有一种是避免产生一致性问题:比如ThreadLocal等,为解决多线的并发问题提供了新的思路。
3.ThreadLocal是什么
定义:提供线程局部变量;一个线程局部变量在多个线程中,分别有独立的值(副本)。
特点:简单(开箱即用)、快速(无额外开销)、安全(线程安全)
场景:多线程环境下,资源持有、线程一致性、并发计算、线程安全等场景。
4.ThreaLocal的API
构造函数ThreadLocal<T>()
初始化 initialValue()
访问器 get/set
回收 remove
ThreadLocal能使线程中的某个值与保存值的对象关联起来。它提供了get和set等访问方法,这些方法为每个使用该变量的线程都存有一份独立的副本,因此get总是返回由当前执行线程在调用set时设置的最新值。
5.ThreadLocal的简单实用
ThreadLocal对象通常用于防止对可变的单实例变量或全局变量进行共享。例如,在单线程应用中可能会维持一个全局的数据库连接,并在程序启动时初始化这个连接对象,从而避免在调用没个方法时都要传递一个Connection对象。由于JDBC的连接对象不一定是线程安全的,因此,当多线程应用程序在没有协同的情况下使用全局变量时,就不是线程安全的。通过将JDBC的连接保存到ThreadLocal对象中,每个线程都会拥有属于自己的连接。
private static ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<Connection>(){
@Override
protected Connection initialValue(){
return DriverManager.getConnection(DB_UR);
}
};
public static Connection getConnection(){
return connectionThreadLocal.get();
}
public class Test { static ThreadLocal<Long> v = new ThreadLocal<Long>(){ @Override protected Long initialValue(){//用于返回该线程局部变量的初始值 return Thread.currentThread().getId(); } }; public static void main(String[] args){ for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(v.get());//返回当前线程对应的线程的局部变量 }).start(); } } }
当某个线程初次调用ThreadLocal的get()方法时,就会调用initialValue()来获取初始值。从概念上看,可以将ThreadLocal<T>理解为包含了Map<Thread,T>对象,其中保存来了特定于该线程的值,但ThreadLocal的实现并非如此。这些特定了线程的值保存在Thread对象中,当线程终止后,这些值会作为垃圾回收。
5.ThreadLocal的原理
ThreadLocal的类图结构:
从类图中我们可以看出,ThreadLocal中有一个内部类ThreadLocalMap,实际上它类似于一个HashMap,用于存放每个线程的变量副本, Map中元素的键为线程对象,而值对应线程的变量副本。
ThreadLocal使用不当,可能会导致内存泄漏问题,隐藏使用完毕后,最好使用ThreadLocal.remove()方法将这个变量移除。
来源:CSDN
作者:男孩李
链接:https://blog.csdn.net/lovebaby1689/article/details/104200550