1、什么是ThreadLocal?
友好式说法:ThreadLocal不是Thread(线程),是线程的局部变量,可以看成是线程的内部存储。每个线程中的ThreadLocal是互相独立的。
卧槽式说法:ThreadLocal就是线程里面的一个变量,把数据存到这个变量里面,在其它地方都可以调用。各个线程之间都是独立存储的,A线程只能访问A的ThreadLocal,B线程只能访问B的ThreadLocal。
ThreadLocal在编写Socket、Http服务的时候是非常常见的。
2、ThreadLocal的应用场景
请先看这样一段代码,没有ThreadLocal时的写法
public class App {
public static void main(String[] args) {
Thread threadA = new Thread(() -> {
String username = "admin";
Request request = new Request();
request.username = username;
doRequest(request);
});
Thread threadB = new Thread(() -> {
String username = "root";
Request request = new Request();
request.username = username;
doRequest(request);
});
threadA.start();
threadB.start();
}
private static void doRequest(Request request) {
request.username = "hello, " + request.username;
doResponse(request);
}
private static void doResponse(Request request) {
System.out.println(request.username);
}
static class Request {
public String username;
}
}
上面一段代码,有木有很像Servlet里面的http请求。username表示前端发送过来的参数,然后构造成Request对象,在doRequest处理逻辑,最后在doResponse中打印出数据。笔者就以这种很简陋的方式来模拟2个http请求,对比使用ThreadLocal和不使用的区别。
那么每次doRequest、doReponse都要把Request对象当做参数传递,如果处理方法超级多,是不是很烦。下面用ThreadLocal来解决这个问题。
public class App {
public static void main(String[] args) {
Thread threadA = new Thread(() -> {
String username = "admin";
Request request = new Request();
request.username = username;
Context.context().setRequest(request);
doRequest();
});
Thread threadB = new Thread(() -> {
String username = "root";
Request request = new Request();
request.username = username;
Context.context().setRequest(request);
doRequest();
});
threadA.start();
threadB.start();
}
private static void doRequest() {
Request request = Context.context().getRequest();
request.username = "hello, " + request.username;
doResponse();
}
private static void doResponse() {
Request request = Context.context().getRequest();
System.out.println(request.username);
}
static class Request {
public String username;
}
static class Context {
private static ThreadLocal<Context> threadLocal = ThreadLocal.withInitial(Context::new);
public static Context context() {
return threadLocal.get();
}
private Request request;
public void setRequest(Request request) {
this.request = request;
}
public Request getRequest() {
return this.request;
}
}
}
从上面代码可以看到用ThreadLocal来存储数据,在当前线程的任何地方都是可以访问的,并且不同线程之间的数据是隔离的。threadA不会看到threadB的数据,反之亦然。 你会了吗?