为什么要有ThreadLocal
我们首先来看看一段最纯粹的原生JDBC代码
可以看到,在使用JDBC时,我们首先要配置后再拿到JDBC连接,然后在增删改查的业务方法中拿到这个连接,并把我们的SQL语句交给JDBC连接发送到真实的DB上执行。
在实际的工作中,我们不会每次执行SQL语句时临时去建立连接,而是会借助数据库连接池,同时因为实际业务的复杂性,为了保证数据的一致性,我们还会引入事务操作,于是上面的代码就会变成:
但是上面的代码包含什么样的问题呢?分析代码我们可以发现,执行业务方法business时,为了启用事务,我们从数据库连接池中拿了一个连接,但是在具体的insert方法和getAll方法中,在执行具体的SQL语句时,我们从数据库连接池中拿一个连接,这就说执行事务和执行SQL语句完全是不同的数据库连接,这会导致什么问题?事务失效了!!数据库执行事务时,事务的开启和提交、语句的执行等都是必须在一个连接中的。实际上,上面的代码要保证数据的一致性,就必须要启用分布式事务。
怎么解决这个问题呢?有一个解决思路是,把数据库连接作为方法的参数,在方法之间进行传递,比如下面这样:
但是我们分析平时我们使用SSM的代码会发现,我们在编写数据访问相关代码的时候从来没有把数据库连接作为方法参数进行传递。这意味着,对Spring来说,在帮我们进行事务托管的时候,会遇到同样的问题,那么Spring是如何解决这个问题的?
其实稍微分析下Spring的事务管理器的代码就能发现端倪,在org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin中,我们会看到如下代码
上面的注释已经很清楚了说明“绑定连接到这个线程”,如何绑定的?继续深入看看
看来,Spring是使用一个ThreadLocal来实现“绑定连接到线程”的。
现在我们可以对ThreadLocal下一个比较确切的定义了
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
此类提供线程局部变量。这些变量与普通对应变量的不同之处在于,访问一个变量