目录
1. 封装一个Util
对于ThreadLocal,
private static final ThreadLocal<HashMap> threadLocal = ThreadLocal.withInitial(() -> new HashMap());
思想首先是初始化一个ThreadLocal,然后又用来存放数据。
public class ThreadLocalUtil {
//初始化threadLocal,存入一个hashMap用于存取数据
private static final ThreadLocal<HashMap> threadLocal = ThreadLocal.withInitial(() -> new HashMap());
public static void add(HashMap map) {
threadLocal.set(map);
}
private static HashMap getMap() {
return threadLocal.get();
}
public static void remove() {
threadLocal.remove();
}
public static void setRequestInfo(String dataSource, String url) {
HttpServletResponse response = (HttpServletResponse) getMap().get("response");
response.setHeader("dataSource", dataSource);
getMap().put("dataSource", dataSource);
getMap().put("url", url);
}
}
这是个基本的思想,可以在自己往里面填充要放入ThreadLocal的东西
比如我Map中有放入Url
public static String getUrl() {
return (String) getMap().get("url");
}
那么在Util 中加入这个方法就可以获取到这个url
这个的生命周期是相对 请求进来,spring来控制管理线程,
然后线程结束,显示的进行remove掉!
2. 更多ThreadLocal
ThreadLocal 用于保存某个线程的共享变量:对于同一个static ThreadLocal 不同线程只能从中get set remove自己的变量
而不会影响到其他线程的变量。
1、ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。
2、ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。
3、ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
4、ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用调用get方法 获取不到Map,返回此方法值。用时进行 重写的,它是一个延迟加载的方法
2.1 原理(很有用)
线程共享变量缓存如下:
Thread.ThreadLocalMap<ThreadLocal, Object>;
1、Thread: 当前线程,可以通过Thread.currentThread()获取。
2、ThreadLocal:我们的static ThreadLocal变量。
3、Object: 当前线程共享变量。
我们调用ThreadLocal.get方法时,实际上是从当前线程中获取ThreadLocalMap<ThreadLocal, Object>,然后根据当前ThreadLocal获取当前线程共享变量Object。
ThreadLocal.set,ThreadLocal.remove实际上是同样的道理。
这种存储结构的好处:
1、线程死去的时候,线程共享变量ThreadLocalMap则销毁。
2、ThreadLocalMap<ThreadLocal,Object>键值对数量为ThreadLocal的数量,一般来说ThreadLocal数量很少,相比在ThreadLocal中用Map<Thread, Object>键值对存储线程共享变量(Thread数量一般来说比ThreadLocal数量多),性能提高很多。
关于ThreadLocalMap<ThreadLocal, Object>弱引用问题:
当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在ThreadLocalMap<null, Object>的键值对,造成内存泄露。(ThreadLocal被回收,ThreadLocal关联的线程共享变量还存在)。
虽然ThreadLocal的get,set方法可以清除ThreadLocalMap中key为null的value,但是get,set方法在内存泄露后并不会必然调用,所以为了防止此类情况的出现,我们有两种手段。
1、使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;
2、JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。
关注点:弱引用 需要定义为priovate static ;然后需要线程使用完,显示调用remove;当线程没有结束,但是ThreadLocal已经被回收这种情况下就会出现 null 和object的键值对!!!
转:https://www.cnblogs.com/coshaho/p/5127135.html
2.2 什么是ThreadLocal
- ThreadLocal,线程本地变量。ThreadLocal为变量在每个线程中创建了一个副本,那么每个线程可以访问自己的线程。 (get 方法获取到自己的线程的local变量)
- 每个线程都可以独立的改变他们的副本不会影响到别的线程
get源码:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
很好理解,就是先获取到当前线程,然后调取:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
t.threadLocals其实就是
ThreadLocal.ThreadLocalMap threadLocals = null;
去找它对应的ThreadLocalMap,也就是2.1所在讲到的,ThreadLocalMap
更准确讲应该是因为t本身就是一个Thread,所以找它内部的成员属性就好~
看源码真是整洁,用this啊 包括一些泛型的使用,还有就是整体的封装性真的高!
2.3 setInitialValue()
/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
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;
}
调用get最后都要调用这个。
补充:
protected T initialValue() {
return null;
}
如果没有获取到map,那么创建一个
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
2.4 其他使用场景
2.4.1 数据库连接
private static ThreadLocal<Connection> connectionHolder= new ThreadLocal<Connection>() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
2.4.2 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;
}
延伸--Thread同步机制
ThreadLocal和线程的同步机制都是为了解决多线程在中相同变量的访问冲突,在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量,这时该变量是多个线程共享的,使用同步机制要求程序=缜密的分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写的难度相对较大。而ThreadLocal则从另一个角度来解决多线程的并发访问,Threadlocal会为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突,因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。
同步机制【以时间换空间】;ThreadLocal【以空间换时间】(毕竟要存很多副本! 这个思想很好 要记下 面试会被问大概)
其他使用场景 转自:https://blog.csdn.net/eieiei438/article/details/87805608
以前的ThreadLocal get()方法等获得的是Object对象,需要进行强制类型转换,那么通过JDK5.0就很好的解决了这个问题。
通过泛型就可以。
2.5 其他不错的博客讲解ThreadLocal
https://www.jianshu.com/p/4e1fcdfb6d54
有空再看深层的原理