多线程-ThreadLocal

1、ThreadLocal
线程本地变量,ThreadLocal 为每个使用该变量得线程提供独立的变量副本,所以每个线程可以独立访问修改自己的副本而不影响其它线程的所对应的副本。

2 、例子
public class ThreadLocalDemo2 {

ThreadLocal<String> tls = new ThreadLocal<>();
ThreadLocal<Long> tll = new ThreadLocal<>();

public void set() {
	tls.set(Thread.currentThread().getName());
	tll.set(Thread.currentThread().getId());
}

public String gets() {
	return tls.get();
}

public Long getl() {
	return tll.get();
}

public static void main(String[] args) {
	ThreadLocalDemo2 demo = new ThreadLocalDemo2();
	
	demo.set();
	System.out.println(demo.gets());
	System.out.println(demo.getl());
	
	new Thread(new Runnable() {
		
		@Override
		public void run() {
			demo.set();
			System.out.println(demo.gets());
			System.out.println(demo.getl());
		}
	},"Thread-33").start();
	
	try {
		Thread.sleep(100);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
	System.out.println(demo.gets());
	System.out.println(demo.getl());
}

}

— 结果
main
1
Thread-33
10
main
1
可以看出,主线程 main 和 线程 Thread-33 两个线程维护着各自的 ThreadLocal,互不干扰。

3 、源码
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
设置值的方法,首先获取到当前线程 t,调用 getMap 方法检查 线程 t 是否存在,存在则把 this 跟 value 存进去,不存在则创建线程 t 的map。

注意 this 是我们 new 出来的 ThreadLocal 对象,然后调用了 set 方法。例如:tls.set(Thread.currentThread().getName());
所以当我们创建了很多线程,这个 this 都是相同的。

ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
ThreadLocal.ThreadLocalMap threadLocals = null;
看看 getMap 方法,把当前线程 t 传进来,返回一个 t.threadLocals。
threadLocals 是 Thread.class 类里面的一个成员变量。类型是 ThreadLocal.ThreadLocalMap。
ThreadLocalMap 是 ThradLocal 类里面的内部类。

static class ThreadLocalMap {

    /**
     * The entries in this hash map extend WeakReference, using
     * its main ref field as the key (which is always a
     * ThreadLocal object).  Note that null keys (i.e. entry.get()
     * == null) mean that the key is no longer referenced, so the
     * entry can be expunged from table.  Such entries are referred to
     * as "stale entries" in the code that follows.
     */
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }

再看这个 ThreadLocalMap 存储的键值对,键值是ThradLocal,value 值是任意对象。

由于第一次进来 getMap(t) 返回是 null,所以就进入到了 createMap(t, value)

void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

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);
}
把 线程 t 和 value 传进来,把新创建的 ThreadLocalMap 赋值给 t.threadLocals。
threadLocals 是 Thread 类里面的一个成员变量,t 就是当前传进来的线程。

所以这里无论有多少个线程,各自的线程里面都维护着自己的 threadLocals 变量,所以各自的线程就变得不受影响。

接着调用了 ThreadLocalMap 的构造方法,key是 ThreadLocal 类型的变量,就像上方例子中,key 是 tls,value 是 main;
key 是 tll,value 是 1。

好了,set 方法就跑完了。
总结一下 set 干了什么:
1、判断 t.threadLocals 是否为空,threadLocals 是 ThradLocalMap 类型的,里面存储着键值对,key 是 ThreadLocal 类型的变量,value 是 T 类型的值。 T 就是 创建 ThreadLocal 传的泛型。
2、如果 ThradLocalMap 不为空,就把 key 是 ThreadLocal 类型的变量,value 是 T 类型的值存进去。
3、如果 ThradLocalMap 为空,就新创建 ThradLocalMap ,把 key 是 ThreadLocal 类型的变量,value 是 T 类型的值存进去,最后赋值给 当前线程 t 中的成员变量 threadLocals。

下面看看 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();
}
首先同样先去获取 ThreadLocalMap,不为空的话,把 this 传进去,返回 value。
如果获取 ThreadLocalMap 为空,则调用 setInitialValue()

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;
}

protected T initialValue() {
return null;
}
setInitialValue 做了两件事,首先创建了一个 value 为 null 的值,然后创建 ThreadLocalMap (这一段跟 set 一样),最后把 value 为 null 返回去。

至此 get 方法就跑完了。

4、总结
所以为什么说使用 ThreadLocal 是线程间变量各不影响,主要是每个线程都有一个成员变量 threadLocals ,里面维护者各自的 map,map里面装着 ThreadLocal 类型的变量,这个变量值每个线程都一样的,但 value 值是不一样的。

使用 ThreadLocal ,调用 get set 方法的时候,必须先调用 set 方法,再调用 get ,否则先 get 的话返回的是 null。不过在创建 ThreadLocal 的时候,可以重写 initialValue,这样就可以先调用 get 方法了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值