在看Looper的源码时,发现有这么个sThreadLocal属性,研究了下
ThreadLocal官方的定义如下
Each thread holds an implicit reference to its copy of a thread-local
variable as long as the thread is alive and the ThreadLocal
instance is accessible; after a thread goes away, all of its copies of
thread-local instances are subject to garbage collection (unless other
references to these copies exist).
大概意思就是每个线程都隐式持有这个本地变量副本,
我这么理解的,针对一个变量N,复制出两个副本NA和NB
分别给线程A和B去操作,彼此互不影响。
插一句,讲到这里难免会想到线程同步并发的事,
但ThreadLocal跟线程同步并发还是有区别的
如果用线程同步来处理并发的情况,需要对对象加锁。多线程操作的是同一个对象
而ThreadLocal上文讲了,操作的实际上是不同的对象。
照着ThreadLocal源码说吧。
获取ThreadLocal存储的副本对象
public T get() {
Thread t = Thread.currentThread();// 当前的线程
ThreadLocalMap map = getMap(t); // 取到当前线程对应的ThreadLocalMap,
if (map != null) { // 取副本对象操作
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
设置 ThreadLocal 存储的副本对象
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value); // this>>ThreadLocal对象,
else
createMap(t, value);
}
private T setInitialValue() {
T value = initialValue(); // 设置的初始化值
Thread t = Thread.currentThread(); // 当前的线程
ThreadLocalMap map = getMap(t); // 取到当前线程对应的ThreadLocalMap,每一个线程对应一个自己的ThreadLocalMap,ThreadLocalMap的key是ThreadLocal对象,value是存储的副本文件
if (map != null)
map.set(this, value);
else
createMap(t, value); // ThreadLocalMap为空时,新生成,讲map和当前线程绑定,生成一个副本
return value;
}
写个demo
public class Test {
int num = 0;
public ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return num;
}
};
public static void main(String[] args) {
Test test = new Test();
CThread ct1 = new CThread(test);
CThread ct2 = new CThread(test);
CThread ct3 = new CThread(test);
new Thread(ct1).start();
new Thread(ct2).start();
new Thread(ct3).start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
finally{
System.out.println(Thread.currentThread().getId() + " >>> " + test.num);
}
}
}
public class CThread implements Runnable {
Test test = null;
public CThread(Test t) {
test = t;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
int v = test.threadLocal.get();
v++;
test.threadLocal.set(v);
System.out.println(
Thread.currentThread().getId() + " >> " + test.threadLocal + " >> " + test.threadLocal.get());
}
}
}
运行结果

可以看到三个线程在各自针对num累加5次,结果都是一样的,证明了操作副本,彼此间互不影响的。而主线程的num仍然是0。
网友评论