1 ThreadLocal是用来做什么的(用来解决什么问题)
ThreadLocal解决了基于类级别的变量定义,每一个线程单独维护自己线程内的变量值;其并不能解决并发问题
2 ThreadLocal的使用及简单实现:
使用:
public class ThreadLocalTest {
private final static ThreadLocal<String> threadLocal=new ThreadLocal<>();
private final static Random random=new Random(System.currentTimeMillis());
public static void main(String[] args)throws Exception {
Thread t1 = new Thread(() -> {
threadLocal.set("thread-T1");
try {
Thread.sleep(random.nextInt(1000));
System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(() -> {
threadLocal.set("thread-T2");
try {
Thread.sleep(random.nextInt(1000));
System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(Thread.currentThread().getName()+""+threadLocal.get());
}
}
简单实现:
public class ThreadLocalSimulator<T> {
private final Map<Thread, T> storge = new HashMap<>();
public void set(T t) {
synchronized (this) {
Thread key = Thread.currentThread();
storge.put(key, t);
}
}
public T get() {
synchronized (this) {
Thread key = Thread.currentThread();
T value = storge.get(key);
if (value == null) {
return null;
}
return value;
}
}
}
3 ThreadLocal的源码的关键实现:
3.1 ThreadLocal的set方法:
源码
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
*1 第一步获取当前线程
*2 利用当前线程获取线程内部的ThreadLocalMap容器,如果没有则创建
*3 将变量副本放进去
注意其中的getmap是利用当前线程获取当前线程内的ThreadLocalMap容器
4.2 ThreadLocal的get方法:
源码
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();
}
*1 获取当前线程
*2 从map中根据this(当前的ThreadLocal对象),获取线程存储的Entry节点
*3 从entry节点中获取对应的value
*4 如果为空走初始化方法,返回初始值null
4.3 ThreadLocal的remove方法:
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
* 1 清除Map中的KV,屏蔽内存泄露
4.4 ThreadLocalMap
ThreadLocalMap是ThreadLoca的静态内部类,其关键实现
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
* ThreadLocalMap是一个定制的哈希映射,仅适用于维护线程本地值。ThreadLocalMap类是包私有的,允许在Thread类中声明字段。为了帮助处理非常大且长时间的使用,哈希表entry使用了对键的弱引用。有助于GC回收(key是使用了弱引用当把threadlocal实例置为null以后没有任何强引用指向threadlocal实例所以threadlocal就可以顺利被gc)。但带来了内存泄露的风险(key被回收了,value还可能在存在)
5 ThreadLocal常见问题:
弱引用的好处?(为何使用弱引用)
* 弱引用会将 key设置为null当使用map的get和set方法时会判断key是否为null如果为null则将value设置为 null。弱引用比强引用在 thread 结束之前多一层屏障。
如何解决弱引用?
* 使用完后,调用threadLocal的remove方法
ThreadLocalMap和Hashmap的区别?
* 解决了hash冲突;HashMap使用的拉链法,而ThreadLocalMap使用的线性探测法(当我们通过 int i = key.threadLocalHashCode&(len-1)计算出hash值,
* 如果出现冲突,顺序查看表中下一单元,直到找出一个空单元或查遍全表)
父子线程共享问题? 待补充