ThreadLocal(线程的局部变量)
1.ThreadLocal是Java1.2提出来的一种对线程的所在执行的线程栈的局部变量
这个方式打印出来的就可以说明一个问题,不同的线程他们的的线程栈是不一样的,换句话当同一个方法被同一个不同的线程调用的时候,他们都会进入各自的线程之间的
栈内存之中。
public class Main { public static void main(String[] args) throws Exception { log("start main..."); new Thread(() -> { log("run task..."); }).start(); new Thread(() -> { log("print..."); }).start(); log("end main."); } static void log(String s) { System.out.println(Thread.currentThread().getName() + ": " + s); } }
现象:
main: start main... Thread-0: run task... main: end main. Thread-1: print...
2.ThreadLocal可以避免在同一个线程之间同一个参数的在不同地方调用(前提是同一个线程),这样可以避免了同一个参数在同一个线程执行中的多次传递;ThreadLocal
实例通常总是以静态字段初始化如下:
static ThreadLocalthreadLocalUser = new ThreadLocal<>();
它的典型使用方式如下:
void processUser(user) { try { threadLocalUser.set(user); step1(); step2(); } finally { threadLocalUser.remove(); }
通过设置一个User
实例关联到ThreadLocal
中,在移除之前,所有方法都可以随时获取到该User
实例:
void step1() { User u = threadLocalUser.get(); log(); printUser(); } void log() { User u = threadLocalUser.get(); println(u.name); } void step2() { User u = threadLocalUser.get(); checkUser(u.id); }
注意到普通的方法调用一定是同一个线程执行的,所以,step1()
、step2()
以及log()
方法内,threadLocalUser.get()
获取的User
对象是同一个实例。
实际上,可以把ThreadLocal
看成一个全局Map
:每个线程获取ThreadLocal
变量时,总是使用Thread
自身作为key:
Object threadLocalValue = threadLocalMap.get(Thread.currentThread());
因此,ThreadLocal
相当于给每个线程都开辟了一个独立的存储空间,各个线程的ThreadLocal
关联的实例互不干扰。最后,特别注意ThreadLocal
一定要在finally
中清除:
try { threadLocalUser.set(user); ... } finally { threadLocalUser.remove(); }
这是因为当前线程执行完相关代码后,很可能会被重新放入线程池中,如果ThreadLocal
没有被清除,该线程执行其他代码时,会把上一次的状态带进去。为了保证能释放ThreadLocal
关联的实例,我们可以通过AutoCloseable
接口配合try (resource) {...}
结构,让编译器自动为我们关闭。例如,一个保存了当前用户名的ThreadLocal
可以封装为一个UserContext
对象:
public class UserContext implements AutoCloseable { static final ThreadLocalctx = new ThreadLocal<>(); public UserContext(String user) { ctx.set(user); } public static String currentUser() { return ctx.get(); } @Override public void close() { ctx.remove(); } }