目录
为什么要有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 access