【数据库连接,线程,ThreadLocal三者之间的关系】

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS Agent(兼容OpenClaw),2核4GB
RDS Agent Manager,2核4GB
简介: 【数据库连接,线程,ThreadLocal三者之间的关系】

一、数据库连接与线程的关系

在实际项目中,数据库连接是很宝贵的资源,以MySQL为例,一台MySQL服务器最大连接数默认是100, 最大可以达到16384。但现实中最多是到200,再多MySQL服务器就承受不住了。因为mysql连接用的是tcp协议,稳定的同时意味着需要消耗更多时间和性能去建立维持连接。为了能最大利用如此宝贵的数据库连接,我们希望数据库连接能被复用而不需要频繁创建,所以利用池化技术建立数据库连接池。

了解连接池在多线程场景中是如何工作的。 一个连接池包含多个连接对象。我们可以配置池的大小。 当多个线程需要并发访问一个数据库时,它们会从连接池中请求连接对象。 如果池中仍有空闲连接,线程将获取连接对象并开始其数据库操作。线程完成工作后,会将连接返回到池中。 如果池中没有空闲连接,线程将等待另一个线程将连接对象返回到池中。 所以,一个连接在同一时间只能被一个线程所持有,一个线程在同一时间也只能申请到一个连接,官方也不鼓励在多个线程之间共享连接对象

明白了这点之后再看下线程,我们知道一个线程在生命周期内会做很多事情,比如参数校验,权限验证,数值计算,然后持久化结果。其中可能只有持久化结果环节需要访问JDBC数据库连接,其余的时间范围内,JDBC数据库连接 都是空闲状态。所以如果线程整个生命周期中独占JDBC数据库连接的话,那么这个连接空闲率就很高也就变得很浪费,因为一旦占用了数据库连接可以不限次数执行事务SQL请求,增删查改各种sql操作请求都可以执行。为了提高数据库连接的使用率,目前普遍的解决方案是:当线程需要做数据库操作时,才会真正请求获取JDBC数据库连接,线程使用完了之后,立即释放,被释放的JDBC数据库连接等待下次分配使用。

二、多线程访问同一个数据库连接

前面已经说了,数据库连接在同一时间只能被一个线程所持有,线程在申请数据库连接时也是线程安全的。

Java多线程访问同一个java.sql.Connection会导致事务错乱。解决多个线程访问同一个Connection对象时,必须遵循两个基本原则:

  • 以资源互斥的方式访问Connection对象;
  • 在线程执行结束时,应当最终及时提交(commit)或回滚(rollback)对Connection的影响;不允许存在尚未被提交或者回滚的语句。

ThreadLocal的原理

想了解下ThreadLocal的原理可以看下这篇文章:ThreadLocal就是这么简单

ThreadLocal和Thread的关系如下图:

ThreadLocal里面定义了ThreadLocalMap这个静态内部类。而Thread类里持有了ThreadLocalMap的引用。

/**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
/**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
• 1
  • ThreadLocal在初始化时会对当前线程所持有的ThreadLocalMap引用进行实例化。
  • ThreadLocal的set方法即是拿出当前线程所持有的ThreadLocalMap引用,然后将所要保存的值塞进这个map里,key是ThreadLocal
  • ThreadLocal的get方法是根据当前线程所持有的ThreadLocalMap引用,用ThreadLocal 这个key拿出value。

三、ThreadLocal 与多线程还有数据库连接的关系

我们经常听说ThreadLocal的目的是为了解决多线程访问资源时的共享问题。这种说法是完全错误的。

ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。

举个例子,一个线程的生命周期要做这么几件事情:参数校验,业务逻辑1,数据持久化,业务逻辑2,数据库切换,业务逻辑3。

==该线程在进行数据持久化时需要申请数据库连接,当做完之后就将连接释放回线程池,但之后进行数据库切换时也需要数据库连接,这时候继续申请吗?不是的,==这时ThreadLocal就派上用场了,在第一次数据持久化操作做完之后,将这个数据库连接暂存在ThreadLocal中,在之后的数据库切换中需要用到时再拿出来用。由此可见,ThreadLocal对线程来说相当于提供一个局部变量,线程把资源用完之后先放到ThreadLocal里,之后需要用到时再拿出来。在多租户场景下,因为经常需要切换数据库,所以ThreadLocal很常用。 那么在多线程场景下ThreadLocal的表现是咋样呢?

如图所示,多线程下,不同线程会持有不同的ThreadLocalMap引用,但这些ThreadLocalMap的key都是指向同个ThreadLocal对象,所以可以理解为什么代码中在用到ThreadLocal时都会设为static了吧,就是为了让多线程下ThreadLocalMap的key都使用的是同一个ThreadLocal,这是JDK1.3之后的改进,这样设计之后每个Map的Entry数量变小了:之前key是Thread,Thread有多少个就会导致key有多少个,现在是ThreadLocal的数量,能提高性能。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
目录
相关文章
|
9月前
|
SQL Java 关系型数据库
Java连接MySQL数据库环境设置指南
请注意,在实际部署时应该避免将敏感信息(如用户名和密码)硬编码在源码文件里面;应该使用配置文件或者环境变量等更为安全可靠地方式管理这些信息。此外,在处理大量数据时考虑使用PreparedStatement而不是Statement可以提高性能并防止SQL注入攻击;同时也要注意正确处理异常情况,并且确保所有打开过得资源都被正确关闭释放掉以防止内存泄漏等问题发生。
443 13
|
9月前
|
SQL 关系型数据库 MySQL
MySQL数据库连接过多(Too many connections)错误处理策略
综上所述,“Too many connections”错误处理策略涉及从具体参数配置到代码层面再到系统与架构设计全方位考量与改进。每项措施都需根据具体环境进行定制化调整,并且在执行任何变更前建议先行测试评估可能带来影响。
1806 11
|
11月前
|
SQL XML Java
配置Spring框架以连接SQL Server数据库
最后,需要集成Spring配置到应用中,这通常在 `main`方法或者Spring Boot的应用配置类中通过加载XML配置或使用注解来实现。
789 0
|
SQL 数据库连接 数据库
在C++的QT框架中实现SQLite数据库的连接与操作
以上就是在C++的QT框架中实现SQLite数据库的连接与操作的基本步骤。这些步骤包括创建数据库连接、执行SQL命令、处理查询结果和关闭数据库连接。在实际使用中,你可能需要根据具体的需求来修改这些代码。
876 14
|
Oracle 安全 关系型数据库
【Oracle】使用Navicat Premium连接Oracle数据库两种方法
以上就是两种使用Navicat Premium连接Oracle数据库的方法介绍,希望对你有所帮助!
2636 28
|
安全 Linux 网络安全
YashanDB数据库服务端SSL连接配置
YashanDB支持通过SSL连接确保数据传输安全,需在服务端生成根证书、服务器证书及DH文件,并将根证书提供给客户端以完成身份验证。服务端配置包括使用OpenSSL工具生成证书、设置SSL参数并重启数据库;客户端则需下载根证书并正确配置环境变量与`yasc_env.ini`文件。注意:启用SSL后,所有客户端必须持有根证书才能连接,且SSL与密码认证独立运行。
|
SQL 缓存 分布式数据库
【赵渝强老师】达梦数据库的线程结构
达梦数据库采用单进程、多线程结构,利用对称服务器架构实现高效资源利用与可扩展性。其核心线程类型包括监听线程(管理客户端连接)、I/O线程(处理数据页读写)、工作线程(执行数据操作)、调度线程(定时任务管理)和日志刷新线程(确保REDO日志刷盘)。通过合理分工与同步机制,达梦数据库实现了高性能与稳定性。视频讲解进一步详细介绍了各线程的功能与协作方式。
431 1
【赵渝强老师】达梦数据库的线程结构
|
Java OLTP 数据库
【YashanDB知识库】如何利用数据库线程池稳定TP吞吐
【YashanDB知识库】如何利用数据库线程池稳定TP吞吐

热门文章

最新文章