ThreadLocal的作用

应用场景

对于请求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被覆盖了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值