public class ThreadLocalTest {
public static void main(String[] args) {
Account account = new Account("初始名");
new MyTest(account,"线程甲").start();
new MyTest(account,"线程乙").start();
}
}
class Account{
private ThreadLocal<String> name = new ThreadLocal<String>();
public String getName(){
return name.get();
}
public void setName(String str){
this.name.set(str);
}
public Account(String str){
this.name.set(str);
System.out.println("---"+this.name.get());
}
}
class MyTest extends Thread{
private Account account;
public MyTest(Account account,String name){
super(name);
this.account = account;
}
@Override
public void run() {
for(int i=0;i<10;i++){
if(i==6){
account.setName(getName());
}
System.out.println(account.getName()+"账户i的值为:"+i);
}
}
}
程序输出为:
当然输出不一定每次都是这样,因为线程调度不可控。我的问题是为什么线程甲乙开始时account.getName()的值为null呢?在main函数里account已经被构造出来了,构造函数里已经有name.set("初始名")了,两个子线程所持有的account的副本应该从一开始name的值就为“初始名”啊,为什么开始是null呢?我哪里理解错了呢?求指导
------解决思路----------------------
看源码就更容易理解了,每个 Thread 中都有一个 Map 属性 threadLocals,ThreadLocal 中的 get(), set() 都是代理给 threadLocals 来设置和获取数据,key 是 Thread 的引用。
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);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
------解决思路----------------------
ThreadLocal 你理解错了,并不是根据已存在的对象创建副本,而是在各自的线程里,通过set方法,创建属于各自线程的对象。
所以,你在构造MyTest 对象时,传递的account对象,它的name属性还是属于主线程的;
你在run的第一行,加上account.setName(getName()) 才是创建子线程自己的name。
所以,你的run方法中,在你调用account.setName(getName());之前,线程并没有创建自己的account,值为null。