如何使用ThreadLocal
public class ThreadLocalDemo {
private static ThreadLocal<String> local = new ThreadLocal<>();
private static String notLocal = new String();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
local.set("我是线程0");
notLocal = "我是线程0";
method();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
local.set("我是线程1");
notLocal = "我是线程1";
method();
}
}).start();
}
public static void method(){
System.out.println(Thread.currentThread().getName()+" local : " + local.get());
System.out.println(Thread.currentThread().getName()+"notLocal : " + notLocal);
}
}
可能出现的结果
Thread-0 local : 我是线程0
Thread-1 local : 我是线程1
Thread-1notLocal : 我是线程1
Thread-0notLocal : 我是线程1
ThreadLocal的内存泄漏问题
要搞清楚这个问题,需要阅读源码
ThreadLocal机制结构
public class ThreadLocal<T> {
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();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
ThreadLocal的运行机制是,在Thread内部有一个Map实例,这个Map实例以ThreadLcoal实例作为key,要保存的对象作为value。如果用使用实例中的代码来说,那就是线程0和线程1中都有一个Map对象,然后这2个map对象都以ThreadLcoal local作为key,线程0的value就是“我是线程0”;线程1的value就是“我是线程1”。
这样一来,就可以形成线程作用域。因为对于ThreadLocal local而言,它在每一个线程中的Map实例里面,都放置了一个String对象。获取的时候,也是从当前线程的map中获取的。
为什么ThreadLocal自己实现了ThreadLocalMap,不使用现成的HashMap等?
别的方面我不清楚,但是最重要的一个原因就是为了解决内存泄漏问题。看ThreadLocalMap的源码可以发现,这个Map实现中,它的key是弱引用。
ThreadLocal为什么会出现内存泄漏问题,别人早就研究的很透彻,且写了很多博客,在这里不再说明。
总之一句话,内存泄漏是因为有不再使用的对象没有被回收,如果ThreadLocalMap中的key,也就是ThreadLocal被回收了,但是Map的value没有被回收,就发生了内存泄漏。如果使用线程被回收,那么自然value也会跟着被回收,影响不大。但如果使用了线程池,Thread就不会被回收,那么value就会一直存在,导致内存泄漏。所以需要掌握正确的ThreadLocal使用姿势。
ThreadLocal 定义,以及是否可能引起的内存泄露(threadlocalMap的Key是弱引用,用线程池有可能泄露)