ThreadLocal的两种实现方式
1.一个ThreadLocal对象内部有一个map,然后在每一个线程中获取值的时候,根据当前线程作为key到map中去获取对应的值
因为ThreadLocal中的值在每个线程中有一个副本,所以这种方式也非常的合适
但是缺点在于,多个线程会一起竞争同一个ThreadLocal对象的map,就会出现竞争,要想避免竞争,就要进行加锁
还有一个需要注意的点就是,如果我们的ThreadLocal的生命周期比Thread长,这种情况下要注意内存的泄漏问题
2.一个ThreadLocal对象中没有任何的map,而是将Map放到了Thread对象中,每个线程获取值的时候,以ThreadLocal做为key到map中去获取
这种方式跟上面是反着过来的,这样有什么好处呢?每个线程访问ThreadLocal的时候,不会出现竞争问题,每个线程访问的是当前线程自己内部的map
避免了加锁
目前的ThreadLocal就是使用的第二种方式实现的,下面我们自己来尝试一下实现这两种方式
第一种
public class CustomThreadLocal<T> {
// 没有使用软引用,重点不在这里
private Map<Thread, T> map = new HashMap<>();
public T get() {
Thread thread = Thread.currentThread();
synchronized (map) {
return map.get(thread);
}
}
public void set(T t) {
Thread thread = Thread.currentThread();
synchronized (map) {
map.put(thread, t);
}
}
/**
* 注意每次ThreadLocal使用结束之后一定要记得进行remove,否则会出现内存泄漏
*
* @param t
*/
public void remove(T t) {
synchronized (map) {
map.remove(t);
}
}
public T initValue() {
return null;
}
}
第二种
自定义的Thread类
public class CustomThread extends Thread {
/**
* 注意,Thread类内部其实已经有了一个map,但是我们不能访问
* 所以这里我们自己定义一个map来实现,主要是为了展示思想
*/
private Map<CustomThreadLocal, Object> map = new HashMap<>();
public CustomThread(Runnable target) {
super(target);
}
Object getValue(CustomThreadLocal local) {
return map.get(local);
}
void set(CustomThreadLocal local, Object value) {
map.put(local, value);
}
void remove(CustomThreadLocal local) {
map.remove(local);
}
}
public class CustomThreadLocal<T> {
public T get() {
Thread t = Thread.currentThread();
if (t instanceof CustomThread) {
CustomThread thread = (CustomThread) t;
T value = (T) thread.getValue(this);
if (value == null) {
value = initValue();
set(value);
}
return value;
}
return null;
}
public void set(T value) {
Thread t = Thread.currentThread();
if (t instanceof CustomThread) {
CustomThread thread = (CustomThread) t;
thread.set(this, value);
}
}
/**
* remove方法非常的重要,在每次使用完Thread对象之后都应该去调用该方法
*/
public void remove() {
Thread t = Thread.currentThread();
if (t instanceof CustomThread) {
CustomThread thread = (CustomThread) t;
thread.remove(this);
}
}
public T initValue() {
return null;
}
}
ThreadLocal的一个使用场景:ThreadContext
在很多时候我们需要在做大量的任务处理的时候,可能会遇到很多地方都需要使用同一个对象的这种情况但是有了ThreadLocal之后,我们有了一种更加优雅的实现方式,每次使用的时候直接向ThreadLocal取而不用在方法之间传来传去