当前位置: 代码迷 >> J2SE >> ThreadLocal的疑点,不是为每一个使用该变量的线程都提供一个变量值的副本吗
  详细解决方案

ThreadLocal的疑点,不是为每一个使用该变量的线程都提供一个变量值的副本吗

热度:119   发布时间:2016-04-23 19:48:09.0
ThreadLocal的疑问,不是为每一个使用该变量的线程都提供一个变量值的副本吗?

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。
  相关解决方案