什么是ThreadLocal?首先要说明的一点是ThreadLocal并非一个Thread,而是Thread的局部变量。在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类能够很简洁地编写出优美的多线程程序。当使用ThreadLocal维护变量时,ThreadLocal为每一个使用该变量的线程提供独立的变量副本,因此每个线程均可以独立地改变本身的副本,而不会影响其它线程所对应的副本。下面咱们就来看看ThreadLocal的初步内容:java
多线程安全性解决方案web
①进行同步控制synchronized效率下降 并发变同步(串行) ,lock(串行)控制灵活spring
②使用ThreadLocal 本地线程 每一个线程一个变量副本(各不相干)数据库
两种线程安全方案的差别安全
归纳起来讲,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而 ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不一样的线程排队 访问,然后者为每个线程都提供了一份变量,所以能够同时访问而互不影响,ThreadLocal占用内存较大,可是速度快,而线程同步相对内存占用小,可是速度慢。若是在内存比较充足的状况,对并发部分的执行效率要求很高的话,那么就是ThreadLocal登场的时候了。通常状况下用同步机制仍是居多的。session
理解ThreadLocal的原理多线程
每一个ThreadLocal对象内部有个ThreadLocalMap,当线程访问ThreadLocal对象时,会在线程内部的ThreadLocalMap新建一个Entry,这样的话每一个线程都有一个对象的副本,保证了并发场景下的线程安全。我理解ThreadLocal的使用场景是某些对象在多线程并发访问时可能出现问题,好比使用SimpleDataFormat的parse()方法,内部有一个Calendar对象,调用SimpleDataFormat的parse()方法会先调用Calendar.clear(),而后调用Calendar.add(),若是一个线程先调用了add()而后另外一个线程又调用了clear(),这时候parse()方法解析的时间就不对了,咱们就能够用ThreadLocal<SimpleDataFormat>来解决并发修改的问题。另外一种场景是Spring事务,事务是和线程绑定起来的,Spring框架在事务开始时会给当前线程绑定一个Jdbc Connection,在整个事务过程都是使用该线程绑定的connection来执行数据库操做,实现了事务的隔离性。Spring框架里面就是用的ThreadLocal来实现这种隔离,代码以下所示:并发
public abstract class TransactionSynchronizationManager {框架
//线程绑定的资源,好比DataSourceTransactionManager绑定是的某个数据源的一个Connection,在整个less
//事务执行过程当中 都使用同一个Jdbc Connection
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");
//事务注册的事务同步器
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal<>("Transaction synchronizations");
//事务名称
private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal<>("Current transaction name");
//事务只读属性
private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal<>("Current transaction read-only status");
//事务隔离级别
private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal<>("Current transaction isolation level");
//事务同步开启
private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal<>("Actual transaction active"); }
不是说一遇到并发场景就用ThreadLocal来解决,咱们还能够用synchronized或者锁来实现线程安全,ThreadLocal使用不当时会引发内存泄露的问题
ThreadLocal就是变量在不一样线程上的副本,不一样线程不共享,因此对变量改动时就不须要考虑线程间同步的问题了
ThreadLocal在web应用开发中是一种很常见的技巧,当web端采用无状态写法时(好比stateless session bean和spring默认的singleton),就能够考虑把一些变量放在ThreadLocal中
举个简单例子,你有两个方法A和B都要用到变量userId,又不想传来传去,一个很天然的想法就是把userId设为成员变量,可是在无状态时,这样作就极可能有问题,由于多个request在同时使用同一个instance,userId在不一样request下值是不同的,就会出现逻辑错误
但因为同一个request下通常都是处于同一个线程,若是放在ThreadLocal的话,这个变量就被各个方法共享了,而又不影响其余request,这种状况下,你能够简单把它理解为是一种没有反作用的成员变量
咱们知道在通常状况下,只有无状态的Bean才能够在多线程环境下共享,在Spring中, 绝大部分Bean均可以声明为singleton做用域。就是由于Spring对一些Bean(如RequestContextHolder、 TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用 ThreadLocal进行处理,让它们也成为线程安全的状态。