多线程环境中,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本;
1 因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来
2 既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题
总的来说,ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景
ThreadLocal维护线程与实例的映射
通过一个例证名threadlocal是每个thread私有
public static class MyRunnable implements Runnable {
private ThreadLocal threadLocal = new ThreadLocal();
@Override
public void run() {
threadLocal.set((int) (Math.random() * 10000));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println(threadLocal.get());
}
}
public static void main(String[] args) {
MyRunnable runnableInstance = new MyRunnable();
Thread thread1 = new Thread(runnableInstance);
Thread thread2 = new Thread(runnableInstance);
thread1.start();
thread2.start();
}
执行结果:
808
1234
既然每个访问 ThreadLocal 变量的线程都有自己的一个“本地”实例副本。一个可能的方案是 ThreadLocal 维护一个 Map,键是 Thread,值是它在该 Thread 内的实例。线程通过该 ThreadLocal 的 get() 方案获取实例时,只需要以线程为键,从 Map 中找出对应的实例即可。该方案如下图所示
看源码刻制 threadlocal内部维护这一个threadlocalmap key值为当前线程,值为Thread内的实例
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 适用于如下两种场景
1 每个线程需要有自己单独的实例;
2 实例需要在多个方法中共享,但不希望被多线程共享;
最后, ThreadLocal 使得代码耦合度更低,且实现更优雅。