Threadlocal的概念
Threadlocal提供了线程本地变量,它可以保证访问到的变量属于当前线程,每个线程都保存有一个变量副本,每个线程的变量都不同,Threadlocal提供了一种线程隔离,将变量与线程绑定
Threadlocal适用于多线程的情况下,可以实现传递数据,实现线程隔离
简单使用
public class Test01 {
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
threadLocal.set("mayikt");
});
thread.start();
thread.join();
System.out.println(threadLocal.get());
}
}
Threadlocal与Synchronized区别
Synchronized和Threadlocal都可以实现多线程访问,保证线程安全问题。
- Synchronized当多个线程竞争到同一个资源的时候最终只能够有一个线程访问,采用时间换空间的方式,保证线程安全问题。
- ThreadLocal在每个线程中都有自己独立的局部变量,空间换时间,相互之间是隔离的,ThreadLocal比Synchronized高。
Threadlocal应用场景
- Spring事务模板类
- 获取httprequest
内存溢出
程序在申请内存时,没有足够的内存空间供其使用,出现out of memory ,oom溢出程序在申请内存时,没有足够的内存空间使用,一般解决,加内存
内存泄漏
指程序在申请内存后,无法释放已申请的内存空间,内存泄漏堆积会导致内存被占光,造成系统内存的浪费,导致程序运行速度减慢甚至崩溃等严重后果。
强、软、弱引用的实现区别
强引用:被引用关联的对象永远不会被垃圾回收器回收
软引用:软引用关联的对象,只有当系统内存溢出时,才会回收软引用的对象
弱引用:被弱引用关联的对象,当垃圾回收机制触发的时候就会被回收
public static void main(String[] args) {
UserEntity entity01 = new UserEntity("11");
//强引用
UserEntity entity03 = entity01;
//弱引用
WeakReference<UserEntity> entity02 = new WeakReference<>(entity01);
entity01 = null;
System.out.println(entity02.get());
System.gc();
System.out.println(entity02.get());
}
Threadlocal原理分析
- 每个线程都有自己独立的ThreadLocalMap对象,底层时Entry对象
- key为当前new ThreadLocal对象,value就是Object变量值
Threadlocal产生内存泄漏问题
因为每个线程中都有自己独立的ThreadLocalMap对象,key为ThreadLocal,value为变量值 ,key是作为Entry对象的key,是弱引用,当ThreadLocal指向null的时候,Entry对象的key变为null,但Entry对象一直无法被垃圾回收机制回收,一直占用了系统内存,可能会造成内存泄漏的问题
如何防御Threadlocal内存泄漏问题
- 可以自己调用remove方法将不用的数据移除避免内存泄漏的问题
- 每次在做set方法的时候会清除之前key为null
Threadlocal采用弱引用而不是强引用
如果key为强引用,当我们现在将ThreadLocal的引用指向为null,但是每个线程中有自己独立ThreadLocalMap,还会一直持有该对象,所以ThreadLocal对象不会被回收,会发生ThreadLocal内存泄漏的问题。
如果key为弱引用,当我们现在将ThreadLocal的引用指向为null,Entry中的key指向为null,下次调用set方法的时候,会根据判断key为空的情况下,直接删除,有可能会发生Entry内存泄漏的问题。
不管强引用还是弱引用都是会发生内存泄漏的问题,弱引用不会发生ThreadLocal内存泄漏的问题。
ThreadLocal内存泄漏的问题,产生于ThreadLocalMap跟我们当前线程的生命周期一样,如果没有手动删除的情况下,就有可能会发生内存泄漏的问题。
ThreadLocal工具类
package com.mayikt;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
/**
* ThreadLocal 工具类
*
* @author :ChenYi
* @date :2020-07-01 16:53
*/
public class ThreadLocalUtil {
private static final ThreadLocal<Map<String, Object>> threadLocal = ThreadLocal.withInitial(() -> new HashMap<>(10));
public static Map<String, Object> getThreadLocal() {
return threadLocal.get();
}
public static <T> T get(String key) {
return get(key, null);
}
public static <T> T get(String key, T defaultValue) {
Map<String, Object> map = threadLocal.get();
return (T) Optional.ofNullable(map.get(key)).orElse(defaultValue);
}
public static void set(String key, Object value) {
Map<String, Object> map = threadLocal.get();
map.put(key, value);
}
public static void set(Map<String, Object> keyValueMap) {
Map<String, Object> map = threadLocal.get();
map.putAll(keyValueMap);
}
public static void remove() {
threadLocal.remove();
}
public static <T> T remove(String key) {
Map<String, Object> map = threadLocal.get();
return (T) map.remove(key);
}
public static void main(String[] args) {
Map<String, Object> map = new HashMap<>();
map.put("11", "1111");
map.put("22", "2222");
ThreadLocalUtil.set("33", "333");
System.out.println(ThreadLocalUtil.getThreadLocal());
ThreadLocalUtil.set(map);
System.out.println(ThreadLocalUtil.getThreadLocal());
Object o = ThreadLocalUtil.get("22");
System.out.println(o);
ThreadLocalUtil.remove("11");
System.out.println(ThreadLocalUtil.getThreadLocal());
ThreadLocalUtil.remove();
System.out.println(ThreadLocalUtil.getThreadLocal());
}
}
参考:蚂蚁课堂