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

many_problems 2015-05-16 07:49:45

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呢?我哪里理解错了呢?求指导
...全文
480 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
confirmAname 2015-05-27
  • 打赏
  • 举报
回复
ThreadLocal确实烦躁啊,帮顶
sirnuo 2015-05-27
  • 打赏
  • 举报
回复
ThreadLocal 你理解错了,并不是根据已存在的对象创建副本,而是在各自的线程里,通过set方法,创建属于各自线程的对象。 所以,你在构造MyTest 对象时,传递的account对象,它的name属性还是属于主线程的; 你在run的第一行,加上account.setName(getName()) 才是创建子线程自己的name。 所以,你的run方法中,在你调用account.setName(getName());之前,线程并没有创建自己的account,值为null。
many_problems 2015-05-17
  • 打赏
  • 举报
回复
引用 1 楼 blueissky 的回复:
public MyTest(Account account,String name){
super(name);
this.account = account;
}

super(name)调用的是父类Thread的构造方法,不是你自己写的Account方法


你说的和我问的关系不大吧?main函数里有三行代码,第一行构造了一个Account对象,在这个Account的构造函数里name属性被set上了“初始名”。然后启动了两个线程,分别叫线程甲乙,那么这两个线程处于就绪状态,谁先得到cpu,谁就先执行,到目前为止没错吧!而且这两个线程都持有account而且是同一个,又因为account的name属性是被ThreadLocal修饰的,所以虽然是同一个account,但是甲乙所持有的account的name属性却是两份,各自独立。而前面这个account的name属性已经被set上了“初始名”,所以线程甲乙中的account.getName()调用时,会返回name.get()的值,因为name有两份,所以线程甲乙开始时name.get()返回的应该都是“初始名”,不是这样吗?问题出在哪里呢?
Inhibitory 2015-05-17
  • 打赏
  • 举报
回复
看源码就更容易理解了,每个 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);
    }
草臻京 2015-05-16
  • 打赏
  • 举报
回复
public MyTest(Account account,String name){ super(name); this.account = account; } super(name)调用的是父类Thread的构造方法,不是你自己写的Account方法

62,612

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧