问题描述
贴上代码:classAccount{/*定义一个ThreadLocal类型的变量,该变量将是一个线程局部变量每个线程都会保留该变量的一个副本*/privateThreadLocal<String>name=newThreadLocal<String>();//定义一个初始化name属性的构造器publicAccount(Stringstr){this.name.set(str);//下面代码用于访问当前线程的name副本的值System.out.println("---"+this.name.get());}//name的setter和getter方法publicStringgetName(){returnname.get();}publicvoidsetName(Stringstr){this.name.set(str);}}classMyTestextendsThread{//定义一个Account属性privateAccountaccount;publicMyTest(Accountaccount,Stringname){super(name);this.account=account;}publicvoidrun(){//循环10次for(inti=0;i<10;i++){//当i==6时输出将账户名替换成当前线程名if(i==6){account.setName(getName());}//输出同一个账户的账户名和循环变量System.out.println(account.getName()+"账户的i值:"+i);}}}publicclassThreadLocalTest{publicstaticvoidmain(String[]args){//启动两条线程,两条线程共享同一个AccountAccountat=newAccount("初始名");/*虽然两条线程共享同一个账户,即只有一个账户名但由于账户名是ThreadLocal类型的,所以每条线程都完全拥有各自的账户名副本,所以从i==6之后,将看到两条线程访问同一个账户时看到不同的账户名。*/newMyTest(at,"线程甲").start();newMyTest(at,"线程乙").start();}}
运行后控制台输出:我的困惑是:为什么在i变为6之前,account.getName()的输出为null?主线程启动的两个线程的构造器里都传入了at,那么,这个account.getName()为什么不输出为“初始名”呢,不是副本吗,副本应该和本体一样才对啊?求高人讲解。。。
解决方案
本帖最后由 fangmingshijie 于 2014-04-02 09:04:52 编辑
解决方案二:
在6之前你没有将name保存到线程的account对象里
解决方案三:
不好意思,看错了,感觉对了啊
解决方案四:
基本上原因就是ThreadLocal中set的对象只能为本线程所看见,不能被其他线程看见对于上面的代码,Account实例化时,成员name.set()的对象只能被main线程取出,而后面的线程甲、乙并不能获取到该Account对象的成员name中set的对象,所以并不会有什么name中存放对象的副本什么的。
解决方案五:
否则如果能看见这个被set的对象,就意味着甲乙线程能看见这个对象的引用,那么当修改对象时,就不能实现甲乙分别设置了一个name,这与ThreadLocal的设计是违背的。
解决方案六:
引用3楼java_liyi的回复:
基本上原因就是ThreadLocal中set的对象只能为本线程所看见,不能被其他线程看见对于上面的代码,Account实例化时,成员name.set()的对象只能被main线程取出,而后面的线程甲、乙并不能获取到该Account对象的成员name中set的对象,所以并不会有什么name中存放对象的副本什么的。
+1
解决方案七:
toptoptop
解决方案八:
引用5楼lq83205823的回复:
Quote: 引用3楼java_liyi的回复:
基本上原因就是ThreadLocal中set的对象只能为本线程所看见,不能被其他线程看见对于上面的代码,Account实例化时,成员name.set()的对象只能被main线程取出,而后面的线程甲、乙并不能获取到该Account对象的成员name中set的对象,所以并不会有什么name中存放对象的副本什么的。+1
我试验过,绝对正解
解决方案九:
三楼是对的threadlocal奥妙在此
解决方案十:
ThreadLocal可以理解为一个Map<Thread_ID,Value>,实例化Account时相当于Map.put(主线程_Id,"初始名");线程甲启动后,在循环输出中name.getName相当于returnMap.get(线程甲_Id),故返回值为null。希望可以帮到你
解决方案十一:
什么情景下使用ThreadLocal比较好?什么情景下使用同步比较好啊?
解决方案十二:
publicTget()返回此线程局部变量的当前线程副本中的值。如果变量没有用于当前线程的值,则先将其初始化为调用initialValue()方法返回的值。返回此线程局部变量的当前线程的“初始值”。线程第一次使用get()方法访问变量时将调用此方法,但如果线程之前调用了set(T)方法,则不会对该线程再调用initialValue方法。通常,此方法对每个线程最多调用一次,但如果在调用get()后又调用了remove(),则可能再次调用此方法。该实现返回null;如果程序员希望线程局部变量具有null以外的值,则必须为ThreadLocal创建子类,并重写此方法。通常将使用匿名内部类完成此操作。
解决方案十三:
引用11楼huanongying131的回复:
publicTget()返回此线程局部变量的当前线程副本中的值。如果变量没有用于当前线程的值,则先将其初始化为调用initialValue()方法返回的值。返回此线程局部变量的当前线程的“初始值”。线程第一次使用get()方法访问变量时将调用此方法,但如果线程之前调用了set(T)方法,则不会对该线程再调用initialValue方法。通常,此方法对每个线程最多调用一次,但如果在调用get()后又调用了remove(),则可能再次调用此方法。该实现返回null;如果程序员希望线程局部变量具有null以外的值,则必须为ThreadLocal创建子类,并重写此方法。通常将使用匿名内部类完成此操作。
+1创建匿名内部类覆盖它的initialValue()方法返回一个默认值就好了,只要你觉得有默认值是设计要求的就可以这么做,多数时候并不需要默认值。