ThreadLocal学习笔记

1.     感觉自己在多线程方面知识很欠缺,平时做WEB开发,体会不是很深,因为Servlet容器、各种WEB框架已经支持了多线程,所以平时很少或者根本不需要考虑到多线程的问题,但最近在学习struts2等框架时,发现这些框架在好多地方都用到了ThreadLocal在处理多线程问题(其实我感觉就是变量传递问题),例如:Struts2中的ActionContext(将变量方便的在pageaction中传递),Hibenate中的ThreadLocalSessionContext(将session跟当前线程关联),于是在网上找了些资料,想弄清楚这个ThreadLocal到底有什么神奇的地方,于是乎有了下面这些学习笔记。

2.    先来看看JDK文档中关于ThreadLocal的解释:

该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

它主要提供了以下2个方法,供调用:

(1)  T  get() 返回此线程局部变量的当前线程副本中的值

(2)  void  set(T  t) 将此线程局部变量的当前线程副本中的值设置为指定值

 

按照我的理解,这个ThreadLocal主要处理多线程中变量的传递,比方说,我们的一个客户请求处理逻辑中,需要跨越很多个类的很多个方法,这些方法都需要使用同一个变量(如map,list

方案1,通常我们的做法是,将这个变量map/list做为方法的参数向下一直传递下去,因为方法的参数相当于方法的局部变量,而局部变量是不存在多线程共同操作的问题;

方案2,可以将map/list放到request中,然后将request做为方法参数向下传递,

显然方案1、方案2存在很大的相似性,而且方案2显然不利于将来的测试,方案1,方案2在方法调用时都需要将变量作为方法参数传递,显然这样的设计不够优美。

于是,出现了ThreadLocal

方案3ThreadLocal可以让我们的变量,做为线程的局部变量,这样在多线程中,不同线程之间的listlist是互不影响的,只需要从ThreadLocal调用get()就可以得到与当前线程关联的list,只要调用set()就可以让list与当前的线程关联,而不需要将list作为方法的参数一直向下传递,

3.    好了,我们来模拟下servlet中使用ThreadLocal来处理变量的传递问题,我们的类列表如下:

MyContext.java  这个就是变量的容器,我们只需要从这里得到变量,而不需要将变量作为参数传递

MyThread.java  线程,在run开始时,将变量跟线程绑定

MyServlet.java   模拟Servlet处理业务逻辑,修改我们的变量

MyPage.java     模拟页面,将变量的值打印出来

Test.java  模拟Servlet容器,用于启动多个线程

public class MyContext

{

    // List<String>的一个对象,作为线程的局部变量

    private static ThreadLocal<List<String>> mycontext = new ThreadLocal<List<String>>();

    private MyContext(){}

    /**

     * ctx跟当前线程绑定

     */

    public static void set(List<String> ctx)

    {

       mycontext.set(ctx);

    }

    /**

     * 返回与当前线程相关的context

     */

    public static List<String> get()

    {

       return mycontext.get();

    }

    /**

     * 直接将str添加到与当前线程相关的context

     */

    public static void add(String str)

    {

       if(str == null)

           return;

       List<String> list = mycontext.get();

       if(list == null)

       {

           list = new ArrayList<String>();

           mycontext.set(list);

       }

       list.add(str);

    }

}

public class MyThread extends Thread

{

    public MyThread(String threadName)

    {

       super(threadName);

    }

    public void run()

    {

       // 创建对象

       List<String> mycontext = new ArrayList<String>();

       // 将对象与当前线程绑定

       MyContext.set(mycontext);

       // 进入另外的调用方法,在其它方法方法里面,通过调用MyContext.get()可以得到与当前线程绑定的MyContext对象

       new MyServlet().execute();

       new MyPage().execute();

    }

}

public class MyServlet

{

    /**

     * 这个方法工作在多线程中

     */

    public void execute()

    {

       // 得到与当前线程相关联的MyContext对象

       List<String> myContext = MyContext.get();

       for(int i = 0; i < 3; i++)

       {

           sleep(10);

           myContext.add(Thread.currentThread().getName() + " - " + new Date().getTime());

       }

    }

    private void sleep(int i)

    {

       try{Thread.sleep(i);}catch (InterruptedException e){e.printStackTrace();}

    }

}

public class MyPage

{

    /**

     *模拟页面输出

     */

    public void execute()

    {

       // 得到与当前线程相关联的MyContext对象

       List<String> myContext = MyContext.get();

       // 执行完后,观察结果

       for(String str : myContext)

           System.out.println(str);

    }

}

public class Test

{

    public static void main(String[] args) throws InterruptedException

    {

       test2();

    }

    /**

     * 模拟Servlet容器,启动3个线程

     */

    private static void test2() throws InterruptedException

    {

       MyThread t1 = new MyThread("线程1");

       MyThread t2 = new MyThread("线程2");

       MyThread t3 = new MyThread("线程3");

       t1.start();

       Thread.sleep(15);

       t2.start();

       Thread.sleep(15);

       t3.start();

    }

}

运行Test.java,输出结果:

线程1 - 1262862012671

线程1 - 1262862012687

线程1 - 1262862012703

线程2 - 1262862012687

线程2 - 1262862012703

线程2 - 1262862012718

线程3 - 1262862012703

线程3 - 1262862012718

线程3 – 1262862012734

 

我们可以看到,线程之间变量是没有影响的。

4.        

posted on 2010-01-07 20:33  TroyZ  阅读(852)  评论(0编辑  收藏  举报