ThreadLocal不能解决多线程同步问题,它提供线程内部的局部变量,在多线程下,这个线程里面的变量不受其他线程的影响。
应用场景:
是在多线程访问多实例(每个线程对应一个实例)的时候,比如说访问网站。服务器会为每个用户开辟一个线程,线程中会存放每个用户的信息,每个线程之间是没有联系的,并且一个用户对应一个线程。在每次跳转时,会读取用户的信息,显示用户的信息,在这种情况下,可以把用户对象放在ThreadLocal中,方便读取操作。
一、早期JDK版本
以web场景举例,早期的ThreadLocal是这样的。有一个表示用户登录信息的HostHolder类。里面维护一个即ThreadLocal的实例类,它是private static修饰的,是类成员变量,它保存的对象是用户类User的实例。要让每个线程都保存一个用户的话,我们想到的方法是ThreadLocal类中维护一个map,key值就是每个线程的id,value值就是User的实例对象。这样在每个线程访问的时候就能保证只访问自己线程对应的value值。
这样map会保存所有线程的值。
public class HostHolder {
private static ThreadLocal<User> users = new ThreadLocal<User>();
public User getUser() {
return users.get();
}
public void setUser(User user) {
users.set(user);
}
public void clear() {
users.remove();
}
}
二、JDK1.8中ThreadLocal是这样实现的
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();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Thread类中包含一个成员变量
ThreadLocal.ThreadLocalMap threadLocals = null;
这段摘自该博客http://blog.youkuaiyun.com/winwill2012/article/details/71625570
1首先获取当前线程
2根据当前线程获取一个Map
3如果获取的Map不为空,则在Map中以ThreadLocal的引用作为key来在Map中获取对应的value e,否则转到5
4如果e不为null,则返回e.value,否则转到5
5Map为空或者e为空,则通过initialValue函数获取初始值value,然后用ThreadLocal的引用和value作为firstKey和firstValue创建一个新的Map
优势:
这样设计之后每个Map的Entry数量变小了:之前是Thread的数量,现在是ThreadLocal的数量,能提高性能,据说性能的提升不是一点两点(没有亲测)
当Thread销毁之后对应的ThreadLocalMap也就随之销毁了,能减少内存使用量。
ThreadLocalMap是ThreadLocal的静态内部类,相当于哈希表。
这和早期的版本有明显不同,早期版本是在ThreadLocal类中维护一个map,key是线程id,根据线程的不同获取线程对应的value值。
改进后的ThreadLocal中没有map了,map放在每个Thread类中,即threadLocals,key值变成了this,也就是每个ThreadLocal类对象,value是线程中的维护的局部变量。比如如下的例子:
public class TestThreadLocal {
private static final ThreadLocal<Integer> value = new ThreadLocal<>();
// private static final ThreadLocal<Integer> value = new ThreadLocal<Integer>() {
// @Override
// protected Integer initialValue() {
// return 0;
// }
// };
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(new MyThread(i)).start();
}
}
static class MyThread implements Runnable {
private int index;
public MyThread(int index) {
this.index = index;
}
public void run() {
value.set(2);
System.out.println("线程" + index + "的初始value:" + value.get());
for (int i = 0; i < 5; i++) { //在各自线程中累加0~5
value.set(value.get() + i);
}
System.out.println("线程" + index + "的累加value:" + value.get());
}
}
}
每个线程维护一个局部整型变量value,在每个线程中对这个变量进行累加,输出的结果不会相互影响。
输出:
线程0的初始value:2
线程4的初始value:2
线程4的累加value:12
线程3的初始value:2
线程1的初始value:2
线程2的初始value:2
线程1的累加value:12
线程3的累加value:12
线程0的累加value:12
线程2的累加value:12
如果是早期的版本,ThreadLocal类是泛型,里面只能存放整型变量。ThreadLocal的value中维护有一个map,线程1到5是map 的key值,具体的value值就是我设置的初始值2。
value.set(2);
然后各自线程维护各自的值,进行累加操作。
如果是新版的需要这么理解:在每个线程中,有一个map哈希表,key值是ThreadLocal的实例对象,上面代码中就是value,上面代码中map只有一个键值对。每个读取就在线程中map读取。
这样的好处据说可以提高效率,线程结束的时候能保证map也被回收。节省内存空间。
参考:
http://blog.youkuaiyun.com/winwill2012/article/details/71625570