- ThreadLocal
/*
* @since 1.2
*/
public class ThreadLocal<T> {
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode = new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
public void set(T value) {
Thread t = Thread.currentThread();
// 根据当前线程 获取ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
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();
}
/*
* @since 1.5
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
/**
* An extension of ThreadLocal that obtains its initial value from
* the specified {@code Supplier}.
*/
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
private final Supplier<? extends T> supplier;
SuppliedThreadLocal(Supplier<? extends T> supplier) {
this.supplier = Objects.requireNonNull(supplier);
}
@Override
protected T initialValue() {
return supplier.get();
}
}
static class ThreadLocalMap {
private static final int INITIAL_CAPACITY = 16;
//实际上就是Object []
private Entry[] table;
//The number of entries in the table
private int size = 0;
private Entry getEntry(ThreadLocal<?> key) {
//根据当前ThreadLocal对象作为Key计算出Entry数组中的下标,获取对应的Entry
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
//每个Entry中存储了一个Object对象
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
}
1.在ThreadLocal中,每一个线程在此处只能存储一个Object对象,此Object是该线程在ThreadLocal中独有的数据。所以一般情况下,当我们需要存储多个变量到Thread中,我们需要定义包含包含这些变量的Object,然后直接存储该Object
2.ThreadLocal 内存泄漏的原因
实心箭头表示强引用,空心箭头表示弱引用
从上图中可以看出,hreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal不存在外部强引用时,Key(ThreadLocal)势必会被GC回收,这样就会导致ThreadLocalMap中key为null, 而value还存在着强引用,只有thead线程退出以后,value的强引用链条才会断掉。
但如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:
Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value
永远无法回收,造成内存泄漏。
广义并通俗的说,就是:不再会被使用的对象或者变量占用的内存不能被回收,就是内存泄露
- 测试案例
public class MyThreadLocal {
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
//不使用ThreadLocal
Pocket instance = Pocket.getInstance();
//使用ThreadLocal
//Pocket instance = Pocket.getInstance2();
int randomData = new Random().nextInt(1000);
instance.setName("Test");
instance.setNum(new Integer(randomData));
System.out.println(randomData);
new ModuleA().getData(instance);
new ModuleB().getData(instance);
}
}).start();
}
}
static class ModuleA {
public void getData(Pocket instance) {
System.out.println("Module A: getData from " + Thread.currentThread().getName()+" Name: "+ instance.getName() + " Num: " + instance.getNum().intValue());
}
}
static class ModuleB {
public void getData(Pocket instance) {
System.out.println("Module B: getData from " + Thread.currentThread().getName()+" Name: "+ instance.getName() + " Num: " + instance.getNum().intValue());
}
}
}
class Pocket {
//不使用 ThreadLocal
private static Pocket instance;
public static Pocket getInstance() {
if(instance == null) {
instance = new Pocket();
}
return instance ;
}
//使用ThreadLocal
// the shared variable in one Thread
private static ThreadLocal<Pocket> mySoldier = new ThreadLocal<Pocket>();
public static Pocket getInstance2() {
Pocket instance = mySoldier.get();
if (instance == null) {
instance = new Pocket();
mySoldier.set(instance);
}
return instance;
}
private String name;
private Integer num;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
}
不使用ThreadLocal,出现因为多线程导致的数据异常
使用ThreadLocal,各个线程只会读取自己在ThreadLocal中所存储对应的Object数据,不会出现数据异常