ThreadLocal是什么?
线程变量,每个线程独享一份。
ThreadLocal的作用
解决线程安全,常用的场景有session的连接,事务的源码
ThreadLocal是怎么样控制线程的安全的?
- 每个ThreadLocal都有一个内部静态类ThreadLocalMap来进行。ThreadLocalMap才用了弱引用当gc开始回收时被销毁。在Entry内部使用ThreadLocal作为key,使用我们设置的value作为value。
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的关键代码
- 源码
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);
}
内存泄露
ThreadLocal的底层使用的是虚引用,会用Entry去存储数据,当某个线程的key为空时只是移除了entry当中的key,其value的指向消失,但是value其还在内存中存在。就会造成内存泄露
使用remove()这个方法,移除整个Entry
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
- 案例演示
/** 线程封闭示例 */
public class ThreadLocalDemo {
/** threadLocal变量,每个线程都有一个副本,互不干扰 */
public static ThreadLocal<String> value = new ThreadLocal<>();
/**
* threadlocal测试
*
* @throws Exception
*/
public void threadLocalTest() throws Exception {
// threadlocal线程封闭示例
value.set("这是主线程设置的123"); // 主线程设置值
String v = value.get();
System.out.println("线程1执行之前,主线程取到的值:" + v);
new Thread(new Runnable() {
@Override
public void run() {
String v = value.get();
System.out.println("线程1取到的值:" + v);
// 设置 threadLocal
value.set("这是线程1设置的456");
v = value.get();
System.out.println("重新设置之后,线程1取到的值:" + v);
System.out.println("线程1执行结束");
}
}).start();
Thread.sleep(5000L); // 等待所有线程执行结束
v = value.get();
System.out.println("线程1执行之后,主线程取到的值:" + v);
}
public static void main(String[] args) throws Exception {
new ThreadLocalDemo().threadLocalTest();
}
}
- 执行结果
线程1执行之前,主线程取到的值:这是主线程设置的123
线程1取到的值:null
重新设置之后,线程1取到的值:这是线程1设置的456
线程1执行结束
线程1执行之后,主线程取到的值:这是主线程设置的123