[心缘地方]同学录
首页 | 功能说明 | 站长通知 | 最近更新 | 编码查看转换 | 代码下载 | 常见问题及讨论 | 《深入解析ASP核心技术》 | 王小鸭自动发工资条VBA版
登录系统:用户名: 密码: 如果要讨论问题,请先注册。

[整理]Java中ThreadLocal的理解

上一篇:[转帖]Java中对任务进行超时限制的办法,Future模式
下一篇:[整理]java中线程同步,ReentrantLock和synchronized的一点区别。

添加日期:2012/5/25 15:58:09 快速返回   返回列表 阅读3437次
有一个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;  
}  


=========================================
大致理解这么多。
 

评论 COMMENTS
没有评论 No Comments.

添加评论 Add new comment.
昵称 Name:
评论内容 Comment:
验证码(不区分大小写)
Validation Code:
(not case sensitive)
看不清?点这里换一张!(Change it here!)
 
评论由管理员查看后才能显示。the comment will be showed after it is checked by admin.
CopyRight © 心缘地方 2005-2999. All Rights Reserved