有一个MultiMethodTest对象,它有setStr和print方法。 我们可以set一个字符串,然后打印它。
一个线程时,当然没问题,set什么就打印什么。 多个线程呢?问题就来啦~~
package thread;
import java.util.Random;
public class MultiMethodTest { static MultiMethodTest test = new MultiMethodTest(); private String str = null; // 单例模式 public static MultiMethodTest getInstance(){ return test; } //get public String getStr() { return str; }
//set synchronized public void setStr(String str) { System.out.println("set "+str +":" + Thread.currentThread().getName()); this.str = str; }
//print synchronized public void print(){ System.out.println(getStr() + ":" + Thread.currentThread().getName()); } /** * @param args */ public static void main(String[] args) {
// 开10个线程 for (int i = 0; i < 10; i++) { final int a = i; new Thread("Thread" + i) { public void run() { // 随机延迟 try { this.sleep(new Random().nextInt(1000)); } catch (InterruptedException e) { }
// set属性 MultiMethodTest.getInstance().setStr("--" + a);
// 随机延迟 try { this.sleep(new Random().nextInt(1000)); } catch (InterruptedException e) { }
// 打印属性 MultiMethodTest.getInstance().print(); } }.start(); } } }
程序输出: ------------- set --6:Thread6 set --5:Thread5 set --0:Thread0 set --3:Thread3 set --9:Thread9 --9:Thread6 set --8:Thread8 --8:Thread0 set --7:Thread7 --7:Thread5 set --4:Thread4 set --2:Thread2 set --1:Thread1 --1:Thread9 --1:Thread8 --1:Thread3 --1:Thread7 --1:Thread2 --1:Thread1 --1:Thread4 ----------------- 某个线程,打印的字符串,并不一定是它set进去的那个。 因为setStr和print是两个方法,两个方法之间可能被其它线程插入,导致内部变量str混乱。
要解决这个问题,单单在两个方法上加synchronized是不好使的。 (1)要么将setStr和print两个方法合二为一。 (2)要么拿另一个方法名包含两个方法,同时添加synchronized关键字。 (3)要么,每次都new一个MultiMethodTest对象 这几个解决办法,好像都不太好。
总结一下,就是: 对于单个对象,它有多个方法,多个方法操作某个成员变量, 怎么保证一个线程执行过程中,另一个线程不会跑进来插一腿,而影响了成员变量的值呢? synchronized只能保证一个方法,同时被一个线程访问,解决不了这个问题。 -------------------------------------------------- 想啊想~~ 想啊想~~~ 要是每个进程执行时,这个成员变量都单独变出来一个,就好了。 靠,这个就是传说中的ThreadLocal哇~~~
改了改,代码如下:
package thread;
import java.util.Random;
public class LocalTest { static LocalTest test = new LocalTest();
private ThreadLocal<String> xxLocal = new ThreadLocal<String>();
// 单例模式 public static LocalTest getInstance() { return test; }
// get public String getStr() { // 从当前线程的背包里,把变量取出来 return xxLocal.get(); }
// set public void setStr(String str) { System.out.println("set " + str + ":" + Thread.currentThread().getName());
// 放到当前线程的小背包里 xxLocal.set(str); }
// print public void print() { System.out.println(getStr() + ":" + Thread.currentThread().getName()); }
/** * @param args */ public static void main(String[] args) {
// 开10个线程 for (int i = 0; i < 10; i++) { final int a = i; new Thread("Thread" + i) { public void run() { // 随机延迟 try { this.sleep(new Random().nextInt(1000)); } catch (InterruptedException e) { }
// set属性 LocalTest.getInstance().setStr("--" + a);
// 随机延迟 try { this.sleep(new Random().nextInt(1000)); } catch (InterruptedException e) { }
// 打印属性 LocalTest.getInstance().print(); } }.start(); } }
}
运行结果如下: --------------------- set --6:Thread6 --6:Thread6 set --9:Thread9 set --1:Thread1 set --2:Thread2 --2:Thread2 set --0:Thread0 --1:Thread1 set --3:Thread3 set --7:Thread7 --9:Thread9 --3:Thread3 set --5:Thread5 set --4:Thread4 set --8:Thread8 --0:Thread0 --7:Thread7 --8:Thread8 --4:Thread4 --5:Thread5 ---------------------- OK了,目的达到了,每个进程都操作自己的变量,互相无影响,synchronized关键字也不用加。
有几个要单独来一份的变量,就要对应用几个ThreadLocal变量。
每个线程对象,都专门为ThreadLocal留了一个map,xxLocal.set和get都是操作这个map呢。所以说,ThreadLocal变量就类似线程的小背包,跟着线程走,随时取放。 ===================================================================== 假设线程是人,方法是屋子,
路人甲,走进屋子A,把一个包子放进背包。 路人甲,走进屋子B,从背包取出包子。 yes,包子通过线程本身,从屋子A传递到了屋子B,这也是ThreadLocal的一个用途。 --------------------------- 路人乙,走进屋子A,把一个包子放进背包。 路人乙,走进屋子B,从背包取出包子。 yes,每个人都有一个单独的包子
这个,当然也看你怎么写代码了, 想每个人都有单独包子的话,在屋子A就要new一个包子,放进背包 想每个人都是同一个包子的话,在屋子A就可以放进一个static的包子~~~ --------------------------- 看一个hibernate中的例子,理解一下吧。 先判断有没有,然后创建,放进去。
private static final ThreadLocal threadSession = new ThreadLocal(); public static Session getSession() throws InfrastructureException { Session s = (Session) threadSession.get(); try { if (s == null) { s = getSessionFactory().openSession(); threadSession.set(s); } } catch (HibernateException ex) { throw new InfrastructureException(ex); } return s; }
========================================= 大致理解这么多。
|