ThreadLocal设计初衷
用于在多线程环境下保证每个线程流水线中所有方法访问的数据不会出现偏差
即不会访问到其它线程中的资源.
其实现方法为:
既然要保持线程与线程间数据的隔离,那么将这些只为本线程服务的数据放在以线程为标识的某个容器之中
ThreadLocal的做法是在Thread类中加了一个属性,这个属性为一个map,可以装任何对象,此map的key类型为ThreadLocal.
ThreadLocal主要提供了两个方法,一个为get,一个为set,分别是以ThreadLocal对象本身作为key将一个对象装入当前线程的map或取出
这样同一个ThreadLocal对象在取相应数据时,在不同的线程中取出的是不同对象
另类经典应用
在web环境中,每一次请求的处理流程以一个流水线的形式于单个线程中执行,
因此一般web容器以线程池的方式对外提供服务.
由于web服务的这个特性,Hibernate运用ThreadLocal保存Session从而实现了线程级别的事务传递.
另外一个例子
由于"同一个ThreadLocal对象在取相应数据时,在不同的线程中取出的是不同对象"
很多人愿意用Filter或Interceptor或BaseAction等方式:
1.在开始处理请求之前将一些线程级共享的数据(session,当前user..)保存到当前线程中去
2.这些ThreadLocal对象为某个类的静态成员
3.这样可以在Dao或Service层中通过它来很方便的获得1中保存的那些引用,从而省略某些参数传递的麻烦
p.1 个人感觉此种各层参数传递时能用传递方式尽量用传递方式比较好
p.2 不过以Aop方式记录日志或加入其它逻辑时如果需要的某些数据并没在target的成员变量里
也没在所拦截到的方法参数里,这时候用ThreadLocal就比较好解决了
不知道还有没有其它比较好的方式去解决
源码解读_thread.threadLocals
每个线程对象有一个成员叫做threadLocals
其类型类为:ThreadLocal.ThreadLocalMap
其访问修饰符为默认,因此只会被同package中的类所访问.或看源码才会发现
源码解读_ThreadLocal<T>
每个线程可以拥有多个ThreadLocal对象
每个ThreadLocal以本身作为一个key保存在map(当前线程对象的的threadLocals属性)之中
ThreadLocal为rawType,可以对应着任何类型的对象,将之存放到map(当前线程对象的的threadLocals属性)之中
源码解读_threadLocal.get()
ThreadLocal类的本方法用于获取存放于map(前线程对象的的threadLocals属性)中的本ThreadLocal对象所对应的对象.
获取方法为以本ThreadLocal对象为key,在map(前线程对象的的threadLocals属性)中取值.
如果map不存在(当前线程对象的threadLocals属性为null):
则于此时创建,并将map中本ThredLocal对象为key所对应的那个value置为null
源码解读_threadLocal.set(T t)
同get(),以对象本身做为一个key,将t保存到map之中以供get()方法将其返回.
同样,如果map不存在,那么将同get()方法中的那样,新创建一个map赋值给当前Thread对象的theadLocals属性.
小实验_多线程数据访问_ThreadLocal
package com.test;
public class TestThreadLocal {
private static ThreadLocal<String> string = new ThreadLocal<String>();
private static ThreadLocal<Integer> integer = new ThreadLocal<Integer>();
public static String getString() {
return string.get();
}
public static Integer getInteger() {
return integer.get();
}
public static void setInteger(Integer value) {
integer.set(value);
}
public static void setString(String value) {
string.set(value);
}
}
package com.test;
import java.util.Random;
public class DoTest {
public static void main(String[] args) {
Thread[] threads = new Thread[100];
for(int i=0;i<100;i++){
threads[i] = new Thread(new MyThread());
}
for(int i=0;i<100;i++){
threads[i].start();
}
}
}
class MyThread implements Runnable{
public void run(){
Random r = new Random();
try {
Thread.sleep(r.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
Integer value = r.nextInt(1000);
TestThreadLocal.setString(value.toString());
TestThreadLocal.setInteger(value);
String result = "Thread_" + value.toString();
try {
Thread.sleep(r.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
result += " : " + TestThreadLocal.getString();
try {
Thread.sleep(r.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
result += " " + TestThreadLocal.getInteger();
System.out.println(result);
}
}
小实验_多线程数据访问_WithOutThreadLocal
package com.test;
import java.util.Random;
public class DoTestWithOutThreadLocal {
public static void main(String[] args) {
Thread[] threads = new Thread[100];
for(int i=0;i<100;i++){
threads[i] = new Thread(new MyThreadWithOutThreadLocal());
}
for(int i=0;i<100;i++){
threads[i].start();
}
}
}
class MyThreadWithOutThreadLocal implements Runnable{
public void run(){
Random r = new Random();
try {
Thread.sleep(r.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
Integer value = r.nextInt(1000);
S.setString(value.toString());
S.setInteger(value);
String result = "Thread_" + value.toString();
try {
Thread.sleep(r.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
result += " : " + S.getString();
try {
Thread.sleep(r.nextInt(10));
} catch (InterruptedException e) {
e.printStackTrace();
}
result += " " + S.getInteger();
System.out.println(result);
}
}
class S{
static Integer integer;
static String string;
public static String getString() {
return string;
}
public static void setString(String string) {
S.string = string;
}
public static Integer getInteger() {
return integer;
}
public static void setInteger(Integer integer) {
S.integer = integer;
}
}