本文转至::[url]http://www.iteye.com/topic/617368[/url]
写这篇帖子的目的不是为了来剖析ThreadLocal,因为坛子里有许多高手已经深入浅出的把ThreadLocal讲解的很清楚了。
特别是lujh99的正确理解ThreadLocal这篇帖子,通过JDK源代码把ThreadLocal讲得非常深入浅出,让我深受启发。我写这篇帖子的目的只是为再此作一个补充,想以另外一种通俗易懂的表达方式把自己对ThreadLocal理解写出来。
lujh99 写道
总之,ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。归纳了两点:
1。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
2。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
正如lujh99 所言,ThreadLocal不是用来解决对象共享访问问题的,而是为了处理在多线程环境中,某个方法处理一个业务,需要递归依赖其他方法时,而要在这些方法中共享参数的问题。例如有方法a(),在该方法中调用了方法b(),而在b方法中又调用了方法c(),即a-->b--->c,如果a,b,c都需要使用用户对象,那么我们常用做法就是a(User user)-->b(User user)---c(User user)。但是如果使用ThreadLocal我们就可以用另外一种方式解决:
在某个接口中定义一个静态的ThreadLocal 对象,例如 public static ThreadLocal threadLocal=new ThreadLocal ();
然后让a,b,c方法所在的类假设是类A,类B,类C都实现1中的接口
在调用a时,使用A.threadLocal.set(user) 把user对象放入ThreadLocal环境
这样我们在方法a,方法b,方法c可以在不用传参数的前提下,在方法体中使用threadLocal.get()方法就可以得到user对象。
上面的类A,类B ,类C就可以分别对应我们做web开发时的 web层的Action--->业务逻辑层的Service-->数据访问层的DAO,当我们要在这三层中共享参数时,那么我们就可以使用ThreadLocal 了。
那么user对象是如何存放到ThreadLocal 中的?
lujh99 写道
将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
经过我的测试,正是如此,当我们调用A.threadLocal.set(user) 时,set()做了以下几件事:
得到当前线程Thread 对象
通过当前线程对象得到当前线程的ThreadLocalMap 对象
将ThreadLocal静态实例作为key,user对象作值,存放到ThreadLocalMap 中。
PS:因为ThreadLocal是静态的,所以每个线程中的ThreadLocalMap的key都是相同的,不同的只是存放的容器ThreadLocalMap。
总结:
其实ThreadLocal 跟我们做web开发时使用的session对象的作用很类似,每当我们向服务器发送一个请求时,web服务器会为该请求创建一个线程,同时会为该请求创建一系列对象,其中包括session(当然在同一个浏览器发送的请求都获得是同一个session对象),所以当我们做web开发时,可以不用担心线程安全问题,自由的往session中存取变量,保存用户状态。同理,当我们在程序中第一次使用A.threadLocal.set(user) 存放参数时,不管在程序的哪个地方,我们都可以通过ThreadLocal 所在的接口访问静态threadLocal对象,同时来共享threadLocal存放的参数,threadLocal就相当于session的作用,来保存当前线程的状态。在我们开发实际开发中,可以任意往threadLocal中共享和存取自己需要的变量,只不过web中的session的生命周期在于客户端的浏览器,而threadLocal中存储的变量的生命周期只在于当前线程,当前结束,threadLocal中存放的参数也被销毁。
在JDK中:::
public class ThreadLocal<T>extends Object该类提供了线程局部变量。这些变量不同于它们的普通对应物,因为访问一个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的私有静态字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
例如,在下面的类中,私有静态 ThreadLocal 实例(serialNum)为调用该类的静态 SerialNum.get() 方法的每个线程维护了一个“序列号”,该方法将返回当前线程的序列号。(线程的序列号是在第一次调用 SerialNum.get() 时分配的,并在后续调用中不会更改。)
public class SerialNum {
// The next serial number to be assigned
private static int nextSerialNum = 0;
private static ThreadLocal serialNum = new ThreadLocal() {
protected synchronized Object initialValue() {
return new Integer(nextSerialNum++);
}
};
public static int get() {
return ((Integer) (serialNum.get())).intValue();
}
}
每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。
写这篇帖子的目的不是为了来剖析ThreadLocal,因为坛子里有许多高手已经深入浅出的把ThreadLocal讲解的很清楚了。
特别是lujh99的正确理解ThreadLocal这篇帖子,通过JDK源代码把ThreadLocal讲得非常深入浅出,让我深受启发。我写这篇帖子的目的只是为再此作一个补充,想以另外一种通俗易懂的表达方式把自己对ThreadLocal理解写出来。
lujh99 写道
总之,ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。归纳了两点:
1。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
2。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
正如lujh99 所言,ThreadLocal不是用来解决对象共享访问问题的,而是为了处理在多线程环境中,某个方法处理一个业务,需要递归依赖其他方法时,而要在这些方法中共享参数的问题。例如有方法a(),在该方法中调用了方法b(),而在b方法中又调用了方法c(),即a-->b--->c,如果a,b,c都需要使用用户对象,那么我们常用做法就是a(User user)-->b(User user)---c(User user)。但是如果使用ThreadLocal我们就可以用另外一种方式解决:
在某个接口中定义一个静态的ThreadLocal 对象,例如 public static ThreadLocal threadLocal=new ThreadLocal ();
然后让a,b,c方法所在的类假设是类A,类B,类C都实现1中的接口
在调用a时,使用A.threadLocal.set(user) 把user对象放入ThreadLocal环境
这样我们在方法a,方法b,方法c可以在不用传参数的前提下,在方法体中使用threadLocal.get()方法就可以得到user对象。
上面的类A,类B ,类C就可以分别对应我们做web开发时的 web层的Action--->业务逻辑层的Service-->数据访问层的DAO,当我们要在这三层中共享参数时,那么我们就可以使用ThreadLocal 了。
那么user对象是如何存放到ThreadLocal 中的?
lujh99 写道
将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
经过我的测试,正是如此,当我们调用A.threadLocal.set(user) 时,set()做了以下几件事:
得到当前线程Thread 对象
通过当前线程对象得到当前线程的ThreadLocalMap 对象
将ThreadLocal静态实例作为key,user对象作值,存放到ThreadLocalMap 中。
PS:因为ThreadLocal是静态的,所以每个线程中的ThreadLocalMap的key都是相同的,不同的只是存放的容器ThreadLocalMap。
总结:
其实ThreadLocal 跟我们做web开发时使用的session对象的作用很类似,每当我们向服务器发送一个请求时,web服务器会为该请求创建一个线程,同时会为该请求创建一系列对象,其中包括session(当然在同一个浏览器发送的请求都获得是同一个session对象),所以当我们做web开发时,可以不用担心线程安全问题,自由的往session中存取变量,保存用户状态。同理,当我们在程序中第一次使用A.threadLocal.set(user) 存放参数时,不管在程序的哪个地方,我们都可以通过ThreadLocal 所在的接口访问静态threadLocal对象,同时来共享threadLocal存放的参数,threadLocal就相当于session的作用,来保存当前线程的状态。在我们开发实际开发中,可以任意往threadLocal中共享和存取自己需要的变量,只不过web中的session的生命周期在于客户端的浏览器,而threadLocal中存储的变量的生命周期只在于当前线程,当前结束,threadLocal中存放的参数也被销毁。
在JDK中:::
public class ThreadLocal<T>extends Object该类提供了线程局部变量。这些变量不同于它们的普通对应物,因为访问一个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的私有静态字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
例如,在下面的类中,私有静态 ThreadLocal 实例(serialNum)为调用该类的静态 SerialNum.get() 方法的每个线程维护了一个“序列号”,该方法将返回当前线程的序列号。(线程的序列号是在第一次调用 SerialNum.get() 时分配的,并在后续调用中不会更改。)
public class SerialNum {
// The next serial number to be assigned
private static int nextSerialNum = 0;
private static ThreadLocal serialNum = new ThreadLocal() {
protected synchronized Object initialValue() {
return new Integer(nextSerialNum++);
}
};
public static int get() {
return ((Integer) (serialNum.get())).intValue();
}
}
每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。