应用场景
对于请求A服务器需要依次执行方法a,b,c....等n个方法(并不一定在同一个类中),其中有多个方法需要使用A上传的某个或某些参数。
问题:对于这些方法如何去获取请求参数?
解决方法:
1.将请求参数当做这些方法的参数传入。缺点就不说了。。。
2.将请求参数封装在一个静态对象B,这样所有的方法都可以访问。缺点:多线程下,存在线程安全问题。
例如当线程Thread_A执行到c方法时,又来一个请求,服务器为响应请求需产生一个线程Thread_B(不管是从线程池取还是新创建的)由对象B为静态,此时对象B的内容将被Thread_B修改,线程ThreadA访问到的将是修改后的内容。
所以这时候需要使用这样一个对象:
在线程内部是静态的共享的,对其他线程是封闭的。
ThreadLocal使用实例:
假设请求参数为User。
创建User类:
public class User {
private String name;
private String password;
public User(String name,String password){
this.name=name;
this.password=password;
}
public String getName() {
return name;
}
public String toString(){
return name+" "+password;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
创建Context类封装请求参数
import java.util.HashMap;
public class Context {
private static ThreadLocal<HashMap<String,Object>>threadLocal=new ThreadLocal<>();
public static void init(HashMap<String,Object>requestParam){
threadLocal.set(requestParam);
System.out.println("线程:"+Thread.currentThread()+" 的请求参数:"+requestParam);
}
public static Object getParamByName(String name){
return threadLocal.get().get(name);
}
}
创建业务逻辑类BusinessService
public class BusinessService {
static void doBusiness(){
System.out.println("线程:"+Thread.currentThread()+" 请求参数:"+Context.getParamByName("user").toString());
}
}
创建测试类ThreadLocalTest
import java.util.HashMap;
public class ThreadLocalTest {
public static void main(String[] args) throws InterruptedException {
HashMap<String,Object>requestParam1=new HashMap<>();//请求1的参数
HashMap<String,Object>requestParam2=new HashMap<>();//请求2的参数
requestParam1.put("user",new User("hello","12345"));
requestParam2.put("user",new User("world","12345678"));
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程:"+Thread.currentThread()+" Context初始化start");
Context.init(requestParam1);
System.out.println("线程:"+Thread.currentThread()+" Context初始化end");
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:"+Thread.currentThread()+" doBusiness start");
BusinessService.doBusiness();
System.out.println("线程:"+Thread.currentThread()+" doBusiness end");
}
});
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000*3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:"+Thread.currentThread()+" Context初始化 start");
Context.init(requestParam2);
System.out.println("线程:"+Thread.currentThread()+" Context初始化 end");
System.out.println("线程:"+Thread.currentThread()+" doBusiness start");
BusinessService.doBusiness();
System.out.println("线程:"+Thread.currentThread()+" doBusiness end");
}
});
thread1.start();
//thread1.join();
thread2.start();
}
}
执行结果:
E:\jdk11\bin\java.exe "-javaagent:E:\IntelliJ IDEA 2019.2.4\lib\idea_rt.jar=52071:E:\IntelliJ IDEA 2019.2.4\bin" -Dfile.encoding=UTF-8 -classpath D:\Learning\Java\spring-framework-5.1.x\test1\out\production\classes;D:\Learning\Java\spring-framework-5.1.x\spring-context\out\production\classes;D:\Learning\Java\spring-framework-5.1.x\spring-context\out\production\resources;D:\Learning\Java\spring-framework-5.1.x\spring-aop\out\production\classes;D:\Learning\Java\spring-framework-5.1.x\spring-aop\out\production\resources;D:\Learning\Java\spring-framework-5.1.x\spring-beans\out\production\classes;D:\Learning\Java\spring-framework-5.1.x\spring-beans\out\production\resources;D:\Learning\Java\spring-framework-5.1.x\spring-expression\out\production\classes;D:\Learning\Java\spring-framework-5.1.x\spring-expression\out\production\resources;D:\Learning\Java\spring-framework-5.1.x\spring-core\out\production\classes;D:\Learning\Java\spring-framework-5.1.x\spring-core\build\libs\spring-cglib-repack-3.2.11.jar;D:\Learning\Java\spring-framework-5.1.x\spring-jcl\out\production\classes;D:\Learning\Java\spring-framework-5.1.x\spring-jcl\out\production\resources;D:\Learning\Java\spring-framework-5.1.x\spring-core\build\libs\spring-objenesis-repack-3.0.1.jar com.leiyza.ThreadLocalTest
线程:Thread[Thread-0,5,main] Context初始化start
线程:Thread[Thread-0,5,main] 的请求参数:{user=hello 12345}
线程:Thread[Thread-0,5,main] Context初始化end
线程:Thread[Thread-1,5,main] Context初始化 start
线程:Thread[Thread-1,5,main] 的请求参数:{user=world 12345678}
线程:Thread[Thread-1,5,main] Context初始化 end
线程:Thread[Thread-1,5,main] doBusiness start
线程:Thread[Thread-1,5,main] 请求参数:world 12345678
线程:Thread[Thread-1,5,main] doBusiness end
线程:Thread[Thread-0,5,main] doBusiness start
线程:Thread[Thread-0,5,main] 请求参数:hello 12345
线程:Thread[Thread-0,5,main] doBusiness end
Process finished with exit code 0
这里,线程1先初始化Context,接着线程2初始化Context并执行doBusiness(),最后线程1执doBusiness()。可以看到,线程2初始化Context操作并没有覆盖掉线程1的请求参数。
不使用ThreadLocal:
修改Context类
import java.util.HashMap;
public class Context {
//private static ThreadLocal<HashMap<String,Object>>threadLocal=new ThreadLocal<>();
private static HashMap<String,Object>params=new HashMap<>();
public static void init(HashMap<String,Object>requestParam){
//threadLocal.set(requestParam);
params=requestParam;
System.out.println("线程:"+Thread.currentThread()+" 的请求参数:"+requestParam);
}
public static Object getParamByName(String name){
//return threadLocal.get().get(name);
return params.get(name);
}
}
结果:
E:\jdk11\bin\java.exe "-javaagent:E:\IntelliJ IDEA 2019.2.4\lib\idea_rt.jar=52289:E:\IntelliJ IDEA 2019.2.4\bin" -Dfile.encoding=UTF-8 -classpath D:\Learning\Java\spring-framework-5.1.x\test1\out\production\classes;D:\Learning\Java\spring-framework-5.1.x\spring-context\out\production\classes;D:\Learning\Java\spring-framework-5.1.x\spring-context\out\production\resources;D:\Learning\Java\spring-framework-5.1.x\spring-aop\out\production\classes;D:\Learning\Java\spring-framework-5.1.x\spring-aop\out\production\resources;D:\Learning\Java\spring-framework-5.1.x\spring-beans\out\production\classes;D:\Learning\Java\spring-framework-5.1.x\spring-beans\out\production\resources;D:\Learning\Java\spring-framework-5.1.x\spring-expression\out\production\classes;D:\Learning\Java\spring-framework-5.1.x\spring-expression\out\production\resources;D:\Learning\Java\spring-framework-5.1.x\spring-core\out\production\classes;D:\Learning\Java\spring-framework-5.1.x\spring-core\build\libs\spring-cglib-repack-3.2.11.jar;D:\Learning\Java\spring-framework-5.1.x\spring-jcl\out\production\classes;D:\Learning\Java\spring-framework-5.1.x\spring-jcl\out\production\resources;D:\Learning\Java\spring-framework-5.1.x\spring-core\build\libs\spring-objenesis-repack-3.0.1.jar com.leiyza.ThreadLocalTest
线程:Thread[Thread-0,5,main] Context初始化start
线程:Thread[Thread-0,5,main] 的请求参数:{user=hello 12345}
线程:Thread[Thread-0,5,main] Context初始化end
线程:Thread[Thread-1,5,main] Context初始化 start
线程:Thread[Thread-1,5,main] 的请求参数:{user=world 12345678}
线程:Thread[Thread-1,5,main] Context初始化 end
线程:Thread[Thread-1,5,main] doBusiness start
线程:Thread[Thread-1,5,main] 请求参数:world 12345678
线程:Thread[Thread-1,5,main] doBusiness end
线程:Thread[Thread-0,5,main] doBusiness start
线程:Thread[Thread-0,5,main] 请求参数:world 12345678
线程:Thread[Thread-0,5,main] doBusiness end
Process finished with exit code 0
可以看到Context被覆盖了。