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

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 【数据库连接,线程,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的数量,能提高性能。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1月前
|
SQL 开发框架 .NET
ASP.NET连接SQL数据库:详细步骤与最佳实践指南ali01n.xinmi1009fan.com
随着Web开发技术的不断进步,ASP.NET已成为一种非常流行的Web应用程序开发框架。在ASP.NET项目中,我们经常需要与数据库进行交互,特别是SQL数据库。本文将详细介绍如何在ASP.NET项目中连接SQL数据库,并提供最佳实践指南以确保开发过程的稳定性和效率。一、准备工作在开始之前,请确保您
161 3
|
22天前
|
关系型数据库 MySQL 数据库连接
python脚本:连接数据库,检查直播流是否可用
【10月更文挑战第13天】本脚本使用 `mysql-connector-python` 连接MySQL数据库,检查 `live_streams` 表中每个直播流URL的可用性。通过 `requests` 库发送HTTP请求,输出每个URL的检查结果。需安装 `mysql-connector-python` 和 `requests` 库,并配置数据库连接参数。
120 68
|
18天前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
32 3
|
25天前
|
关系型数据库 MySQL 数据库连接
DBeaver如何连接一个数据库
【10月更文挑战第27天】DBeaver 是一款功能强大的通用数据库管理工具,支持多种主流数据库。本文介绍了使用 DBeaver 连接数据库的基本步骤,包括下载安装、创建新连接、选择数据库类型、配置连接参数、测试连接以及最终连接到数据库。详细的操作指南帮助用户轻松管理和操作数据库。
128 9
|
18天前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
16 1
|
18天前
|
Java 数据库连接 数据库
Java连接池在数据库性能优化中的重要作用。连接池通过预先创建和管理数据库连接,避免了频繁创建和关闭连接的开销
本文深入探讨了Java连接池在数据库性能优化中的重要作用。连接池通过预先创建和管理数据库连接,避免了频繁创建和关闭连接的开销,显著提升了系统的响应速度和吞吐量。文章介绍了连接池的工作原理,并以HikariCP为例,展示了如何在Java应用中使用连接池。通过合理配置和优化,连接池技术能够有效提升应用性能。
33 1
|
1月前
|
Java 关系型数据库 MySQL
如何用java的虚拟线程连接数据库
本文介绍了如何使用Java虚拟线程连接数据库,包括设置JDK版本、创建虚拟线程的方法和使用虚拟线程连接MySQL数据库的示例代码。
45 6
如何用java的虚拟线程连接数据库
|
26天前
|
SQL JavaScript 关系型数据库
node博客小项目:接口开发、连接mysql数据库
【10月更文挑战第14天】node博客小项目:接口开发、连接mysql数据库
|
1月前
|
SQL Java 数据库连接
如何使用`DriverManager.getConnection()`连接数据库,并利用`PreparedStatement`执行参数化查询,有效防止SQL注入。
【10月更文挑战第6天】在代码与逻辑交织的世界中,我从一名数据库新手出发,通过不断探索与实践,最终成为熟练掌握JDBC的开发者。这段旅程充满挑战与惊喜,从建立数据库连接到执行SQL语句,再到理解事务管理和批处理等高级功能,每一步都让我对JDBC有了更深的认识。示例代码展示了如何使用`DriverManager.getConnection()`连接数据库,并利用`PreparedStatement`执行参数化查询,有效防止SQL注入。
87 5
|
1月前
|
Java 关系型数据库 MySQL
springboot学习五:springboot整合Mybatis 连接 mysql数据库
这篇文章是关于如何使用Spring Boot整合MyBatis来连接MySQL数据库,并进行基本的增删改查操作的教程。
71 0
springboot学习五:springboot整合Mybatis 连接 mysql数据库