ThreadLocal介绍

并发程序最关键的一方面就是数据共享。当你创建了一个实现了Runable

ThreadLocal为每个使用它的线程提供单独的线程局部变量副本,每个线程都只能看到与自己关联的值,而不知道其他线程可能正在使用或者修改它们自己的副本。

ThreadLocal采用空间换时间的方式来解决线程安全问题。ThreadLocal会把共享变量复制一份到本地线程内存中,然后在自己的线程内对变量副本进行操作,各个线程只操作自己的变量副本而不影响其他线程的变量副本。

ThreadLocal 使用

代码一:

/**
* @author Aaron
* @Date 2019/4/16 21:42
**/
public class ThreadLocalBean {

private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 10;
}
};


public Integer get(){
return threadLocal.get();
}

public void set(){
threadLocal.set(get() + 10);
}


public static void main(String[] args) {
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
System.out.println(threadLocal.get());
}
}

代码二:

/**
* @author Aaron
* @Date 2019/4/15 16:40
**/
public class ThreadLocalDemo {

public static void main(String[] args) {
ThreadLocalBean bean = new ThreadLocalBean();
TempThread tempThread1 = new TempThread(bean);
TempThread tempThread2 = new TempThread(bean);
Thread thread1 = new Thread(tempThread1 , "thread1");
Thread thread2 = new Thread(tempThread2 , "thread2");

thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("bean value:"+bean.get());
}


static class TempThread implements Runnable{
ThreadLocalBean bean;

public TempThread(ThreadLocalBean bean){
this.bean = bean;
}

@Override
public void run() {
for (int i = 0; i < 5; i++) {
bean.set();
}
System.out.println(Thread.currentThread().getName()+"---"+bean.get());
}
}

}
运行结果
thread2---60
thread1---60
bean value:10

从运行结果我们发现,在thread2和thread1分别调用同一个ThreadLocalBean 的set()方法,两者的运行结果相同并且互不影响,同时这两线程对bean的操作也没有影响到bean的ThreadLocal里的值。

ThreadLocal使用场景

当你在做一个电商应用的时候,你有一个需求是为每个用户访问controller时生成一个唯一的事务ID,并且为了登录需要把这个事务ID传给业务逻辑层。一个解决方案就是把事务ID传给所有业务方法,这样的话代码就是变得冗余也没必要这样。

为了解决这个,你可以使用ThreadLocal局部变量。你可以在controller 或者预处理拦截器中生成一个事务ID,然后把它设置到ThreadLocal变量里面,之后不管什么方法调用controller里面方法,它们都能够从ThreadLocal获取到事务ID。这样controller将会处理更多的请求,并且每个请求在框架层面都是互相隔离的,事务id对每个线程都是惟一的,并且可以从线程的所有执行路径访问它