public class Main {
public static void main(String[] args) {
ThreadLocal<String> threadLocal1 = new ThreadLocal<>();
ThreadLocal<String> threadLocal2 = new ThreadLocal<>();
new Thread(new Runnable() {
@Override
public void run() {
threadLocalOne.set("Thread-1: --- threadLocal1 ");
threadLocalTwo.set("Thread-1: --- threadLocal2 ");
System.out.println(threadLocal1.get());
System.out.println(threadLocal2.get());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(threadLocal1.get());
System.out.println(threadLocal2.get());
threadLocalOne.set("Thread-2: --- threadLocal1 ");
threadLocalTwo.set("Thread-2: --- threadLocal2 ");
System.out.println(threadLocalOne.get());
System.out.println(threadLocalTwo.get());
}
}).start();
}
}
/*
Thread-1: --- threadLocal1
Thread-1: --- threadLocal2
null
null
Thread-2: --- threadLocal1
Thread-2: --- threadLocal2
*/
简单易用,而且还实现了线程隔离;
使用ThreadLocal在不同的线程中进行写操作,实际上数据都是绑定在当前线程的实例上,ThreadLocal.只负责读写操作,并不负责保存数据,这就解释了,为什么ThreadLocal.set数据,只在操作的线程中有用;
// ThreadLocal.java
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocal.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();
}
ThreadLocal.ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//Thread.java
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
}
上述代码就实现了线程隔离,将ThreadLocalMap和Thread一对一绑定,而ThreadLoal仅仅是作为ThreadLocalMap对外的管理员而已,
这是一个弱引用,以ThreadLocal作为key,以需要存入的对象 T为value。这里需要强调一点的是虽然这是一个弱引用,但是对于ThreadLocal来说,它自己要产生对象是new出来的,所以对于ThreadLocal自己肯定是强引用;
一个线程可能存在多个ThreadLocal,但是ThreadLocalMap只有一份。get、set方法针对当前线程操作目标对象,每次执行都会先将之前的Entry做一次回收,key为弱引用的目的是当线程执行完毕后,若下次有GC发生,那么线程执行完毕后的该Entry对象可以自行回收掉。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry的key引用被gc回收,需要先手动断开ThreadLocal本身的强引用
static ThreadLocal<Object> threadLocal1 = new ThreadLocal<>();
public static void main(String[] args) {
new Thread(()->{
threadLocal1.set(new Object());
try {
threadLocal1 = null;
System.gc();
//下面代码来自:https://blog.csdn.net/thewindkee/article/details/103726942
Thread t = Thread.currentThread();
Class<? extends Thread> clz = t.getClass();
Field field = clz.getDeclaredField("threadLocals");
field.setAccessible(true);
Object threadLocalMap = field.get(t);
Class<?> tlmClass = threadLocalMap.getClass();
Field tableField = tlmClass.getDeclaredField("table");
tableField.setAccessible(true);
Object[] table = (Object[]) tableField.get(threadLocalMap);
for (Object entry : table) {
if (entry == null) continue;
Class<?> entryClass = entry.getClass();
Field valueField = entryClass.getDeclaredField("value");
Field referenceField = entryClass.getSuperclass().getSuperclass().getDeclaredField("referent");
valueField.setAccessible(true);
referenceField.setAccessible(true);
System.out.printf("弱引用key:%s 值:%s%n", referenceField.get(entry), valueField.get(entry));
}
} catch (Exception e) { }
}).start();
}
/*
注:多出来的两个,说明该线程中还有另外两个ThreadLocal,反正看不见,也不影响当前ThreadLocal使用,不管了
弱引用key:null 值:java.lang.Object@54da927
弱引用key:jdk.internal.jimage.ImageBufferCache$1@431b1659 值:[Ljdk.internal.jimage.ImageBufferCache$BufferReference;@5690d712
弱引用key:java.lang.ThreadLocal@258bbd70 值:java.lang.ref.SoftReference@4d673c77
*/
维护了一个Entry数组table,它比较核心一点的地方是,自身是一个环形结构,nextIndex方法,将传入的index+1,当index长度超过数组长度后,会直接返回0,又回到数组头部,这就完成一个环形结构
public class ThreadLocal<T> {
private Entry[] table;
...
static class ThreadLocalMap {
...
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
...
}
}
ThreadLocal的set,get和remove方法看下来,除了正常的功能之外,就是多了对key为null的entry的清理工作,方便GC回收这部分占用的内存。expungeStaleEntry就是最核心的清理方法,这也是ThreadLocalMap的一种防范机制,因为ThreadLocalMap的生命周期和线程是一样长。内存泄露就是创建了太多的ThreadLocal变量,然后呢,又没有及时的释放内存;内存溢出可以理解为创建了多个ThreadLocal变量,然后又给它们分配了占用内存比较大的对象,使得多个线程累计占用太多内存,导致系统出现内存溢出。
remove方法主要是为了防止内存溢出和内存泄露,使用的时机一般是在线程运行结束之后使用,也就是run()方法结束之后。