ThreadLocal知识点
前言
线程隔离:在多线程并发的场景下,线程中的每个变量都相互独立
一、ThreadLocal入门案例。
public class MyLocalThread {
ThreadLocal<String> myLocalThread = new ThreadLocal<>();
private String content;
public String getContent() {
return myLocalThread.get();
}
public void setContent(String content) {
myLocalThread.set(content);
}
public static void main(String[] args) {
MyLocalThread myLocalThread = new MyLocalThread();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
myLocalThread.setContent(Thread.currentThread().getName());
System.out.println("--------------------------");
System.out.println("线程名字:" + Thread.currentThread().getName() + "----->内容:" + myLocalThread.getContent());
}, i + "").start();
}
}
}
//这样是有问题的
public class MyLocalThread {
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public static void main(String[] args) {
MyLocalThread myLocalThread = new MyLocalThread();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
myLocalThread.setContent(Thread.currentThread().getName() + "的线程");
System.out.println("--------------------------");
System.out.println("线程名字:" + Thread.currentThread().getName() + "----->内容:" + myLocalThread.getContent());
}, i + "").start();
}
}
}
二、synchronized 与ThreadLocal 的区别。
synchronized 时间换空间 让每个线程排队访问,串行
ThreadLocal 空间换时间,让每个线程之间数据相互隔离,互不干扰,每个线程都有一个变量副本,并行
三、ThreadLocal 的优势。
1、传递数据︰保存每个线程绑定的数据,在需要的地方可以直接获取,避免参数直接传递带来的代码耦合问题
2、线程隔离∶各线程之间的数据相互隔离却又具备并发性,避免同步方式带来的性能损失
四、ThreadLocal 内部结构
常见误解:ThreadLocal 底层是不是一个map,早期是map。线程作为key,要存储的局部变量作为val。
myLocalThread.set(content); 方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//对应得 key value
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
( 2)代码执行流程
A.首先获取当前线程,并根据当前线程获取一个Map
B.如果获取的Map不为空,则将参数设置到Map中(当前ThreadLocal的引用作为key )
C.如果Map为空,则给该线程创建Map ,并设置初始值
myLocalThread.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变量,如果存在则返回值,不存在则创建并返回初始值。
initialValue() 方法
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;
}
protected T initialValue() {
return null;
}
五、内存溢出和内存泄漏 概念
内存溢出:(out of memory)通俗理解就是内存不够,通常在运行大型软件或游戏时,软件或游戏所需要的内存远远超出了你主机内安装的内存所承受大小,就叫内存溢出。
内存泄漏:(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果
六、ThreadLocal正确的使用方法
每次使用完ThreadLocal都调用它的remove()方法清除数据
将ThreadLocal变量定义成private static,这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉
七、实战