java jdbc深入理解(connection与threadlocal与数据库连接池和事务实)
1.jdbc连接数据库,就这样子
Class.forName("com.mysql.jdbc.Driver");
java.sql.Connection conn = DriverManager.getConnection(jdbcUrl);
2.通过传入jdbc url用Drivermanager.getConnection(jdbcurl)连接数据库,
注意:一次Drivermanager.getConnection(jdbcurl)获得只是一个connection,并不能满足高并发情况。因为connection不是线程安全的,一个connection对应的是一个事物。
3.所以数据库连接池,是多次Drivermanager.getConnection(jdbcurl),获取多个connection放入hashmap中。
4.每次获得connection都需要浪费cpu资源和内存资源,是很浪费资源的。所以诞生了数据库连接池。
5.数据库连接池部分源码:
注意pool.getConnection(),都是先从threadlocal里面拿的,如果threadlocal里面有,则用,保证线程里的多个dao操作,用的是同一个connection,以保证事务。
如果新线程,则将新的connection放在threadlocal里,再get给到线程。
着重看以下几个方法,说明数据库连接池,是将connection放进threadlocal里的,以保证每个线程从连接池中获得的都是线程自己的connection。
- // 将线程和连接绑定,保证事务能统一执行
- 成员变量 private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
// 获得当前连接
public Connection getCurrentConnecton(){
// 默认线程里面取
Connection conn = threadLocal.get();
if(!isValid(conn)){
conn = getConnection();
}
return conn;
}
// 获得连接
public synchronized Connection getConnection() {
Connection conn = null;
try {
// 判断是否超过最大连接数限制
if(contActive < this.dbBean.getMaxActiveConnections()){
if (freeConnection.size() > 0) {
conn = freeConnection.get(0);
if (conn != null) {
threadLocal.set(conn);
}
freeConnection.remove(0);
} else {
conn = newConnection();
} }else{
// 继续获得连接,直到从新获得连接
wait(this.dbBean.getConnTimeOut());
conn = getConnection();
}
if (isValid(conn)) {
activeConnection.add(conn);
contActive ++;
}
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return conn;
}
public synchronized void releaseConn(Connection conn) throws SQLException {
if (isValid(conn)&& !(freeConnection.size() > dbBean.getMaxConnections())) {
freeConnection.add(conn);
activeConnection.remove(conn);
contActive --;
threadLocal.remove();
// 唤醒所有正待等待的线程,去抢连接
notifyAll();
}
}
然后再着重理解此段话

首先,LZ是概念上的错误.什么是线程池,什么是ThreadLocal???
线程池,为避免不必要的创建,销毁connection而存在的,其中包括活动,等待,最小等属性,cop3,proxy连接池都可以配置这些玩意; 至于为什么要用ThreadLocal呢?这个和连接池无关,我认为更多的是和程序本身相关,为了更清楚的说明,我举个例子
servlet中获取一个连接.首先,servlet是线程安全的吗?
class MyServlet extends HttpServlet{
private Connection conn;
}
ok,遗憾的告诉你,这个conn并不是安全的,所有请求这个servlet的连接,使用的都是一个Connection,这个就是致命的了.多个人使用同一个连接,算上延迟啥的,天知道数据会成什么样.
因此我们要保证Connection对每个请求都是唯一的.这个时候就可以用到ThreadLocal了,保证每个线程都有自己的连接.
改为 private ThreadLocal<Connection> ct = new ThreadLocal<Connnection>();
然后从连接池获取Connection,set到ct中,再get就行了,至于得到的是哪个Connection就是连接池的问题了,你也管不到.

Hibernate的数据库连接池就是将connection放进threadlocal实现的!!!
Hibernate的数据库连接池就是将connection放进threadlocal实现的!!!
Hibernate的数据库连接池就是将connection放进threadlocal实现的!!!

public class ConnectionPool implements IConnectionPool {
// 连接池配置属性
private DBbean dbBean;
private boolean isActive = false; // 连接池活动状态
private int contActive = 0;// 记录创建的总的连接数 // 空闲连接
private List<Connection> freeConnection = new Vector<Connection>();
// 活动连接
private List<Connection> activeConnection = new Vector<Connection>();
- // 将线程和连接绑定,保证事务能统一执行
- private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
public ConnectionPool(DBbean dbBean) {
super();
this.dbBean = dbBean;
init();
cheackPool();
} // 初始化
public void init() {
try {
Class.forName(dbBean.getDriverName());
for (int i = 0; i < dbBean.getInitConnections(); i++) {
Connection conn;
conn = newConnection();
// 初始化最小连接数
if (conn != null) {
freeConnection.add(conn);
contActive++;
}
}
isActive = true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
} // 获得当前连接
public Connection getCurrentConnecton(){
// 默认线程里面取
Connection conn = threadLocal.get();
if(!isValid(conn)){
conn = getConnection();
}
return conn;
} // 获得连接
public synchronized Connection getConnection() {
Connection conn = null;
try {
// 判断是否超过最大连接数限制
if(contActive < this.dbBean.getMaxActiveConnections()){
if (freeConnection.size() > 0) {
conn = freeConnection.get(0);
if (conn != null) {
threadLocal.set(conn);
}
freeConnection.remove(0);
} else {
conn = newConnection();
} }else{
// 继续获得连接,直到从新获得连接
wait(this.dbBean.getConnTimeOut());
conn = getConnection();
}
if (isValid(conn)) {
activeConnection.add(conn);
contActive ++;
}
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return conn;
} // 获得新连接
private synchronized Connection newConnection()
throws ClassNotFoundException, SQLException {
Connection conn = null;
if (dbBean != null) {
Class.forName(dbBean.getDriverName());
conn = DriverManager.getConnection(dbBean.getUrl(),
dbBean.getUserName(), dbBean.getPassword());
}
return conn;
} // 释放连接
public synchronized void releaseConn(Connection conn) throws SQLException {
if (isValid(conn)&& !(freeConnection.size() > dbBean.getMaxConnections())) {
freeConnection.add(conn);
activeConnection.remove(conn);
contActive --;
threadLocal.remove();
// 唤醒所有正待等待的线程,去抢连接
notifyAll();
}
} // 判断连接是否可用
private boolean isValid(Connection conn) {
try {
if (conn == null || conn.isClosed()) {
return false;
}
} catch (SQLException e) {
e.printStackTrace();
}
return true;
} // 销毁连接池
public synchronized void destroy() {
for (Connection conn : freeConnection) {
try {
if (isValid(conn)) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
for (Connection conn : activeConnection) {
try {
if (isValid(conn)) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
isActive = false;
contActive = 0;
} // 连接池状态
@Override
public boolean isActive() {
return isActive;
} // 定时检查连接池情况
@Override
public void cheackPool() {
if(dbBean.isCheakPool()){
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// 1.对线程里面的连接状态
// 2.连接池最小 最大连接数
// 3.其他状态进行检查,因为这里还需要写几个线程管理的类,暂时就不添加了
System.out.println("空线池连接数:"+freeConnection.size());
System.out.println("活动连接数::"+activeConnection.size());
System.out.println("总的连接数:"+contActive);
}
},dbBean.getLazyCheck(),dbBean.getPeriodCheck());
}
}
}

Why ThreadLocal?
无论如何,要编写一个多线程安全(Thread-safe)的程序是困难的,为了让线程共享资源,必须小心地对共享资源进行同步,同步带来一定的效能延迟,而另一方面,在处理同步的时候,又要注意对象的锁定与释放,避免产生死结,种种因素都使得编写多线程程序变得困难。
尝试从另一个角度来思考多线程共享资源的问题,既然共享资源这么困难,那么就干脆不要共享,何不为每个线程创造一个资源的复本。将每一个线程存取数据的行为加以隔离,实现的方法就是给予每个线程一个特定空间来保管该线程所独享的资源
什么是ThreadLocal?
顾名思义它是local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。
使用场景
- To keep state with a thread (user-id, transaction-id, logging-id)
- To cache objects which you need frequently
ThreadLocal类 实现线程范围的共享变量
它主要由四个方法组成initialValue(),get(),set(T),remove(),其中值得注意的是initialValue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初始值,这个方法是一个延迟调用方法,在一个线程第1次调用get()或者set(Object)时才执行,并且仅执行1次。ThreadLocal中的确实实现直接返回一个null:
ThreadLocal的原理
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现:
public class ThreadLocal
{
private Map values = Collections.synchronizedMap(new HashMap());
public Object get()
{
Thread curThread = Thread.currentThread();
Object o = values.get(curThread);
if (o == null && !values.containsKey(curThread))
{
o = initialValue();
values.put(curThread, o);
}
return o;
}
public void set(Object newValue)
{
values.put(Thread.currentThread(), newValue);
}
public Object initialValue()
{
return null;
}
}
ThreadLocal 的使用
使用方法一:
hibernate的文档时看到了关于使ThreadLocal管理多线程访问的部分。具体代码如下
1. public static final ThreadLocal session = new ThreadLocal();
2. public static Session currentSession() {
3. Session s = (Session)session.get();
4. //open a new session,if this session has none
5. if(s == null){
6. s = sessionFactory.openSession();
7. session.set(s);
8. }
return s;
9. }
我们逐行分析
1。 初始化一个ThreadLocal对象,ThreadLocal有三个成员方法 get()、set()、initialvalue()。
如果不初始化initialvalue,则initialvalue返回null。
3。 session的get根据当前线程返回其对应的线程内部变量,也就是我们需要的net.sf.hibernate.Session(相当于对应每个数据库连接).多线程情况下共享数据库链接是不安全的。ThreadLocal保证了每个线程都有自己的s(数据库连接)。
5。如果是该线程初次访问,自然,s(数据库连接)会是null,接着创建一个Session,具体就是行6。
6。创建一个数据库连接实例 s
7。保存该数据库连接s到ThreadLocal中。
8。如果当前线程已经访问过数据库了,则从session中get()就可以获取该线程上次获取过的连接实例。
使用方法二
当要给线程初始化一个特殊值时,需要自己实现ThreadLocal的子类并重写该方法,通常使用一个内部匿名类对ThreadLocal进行子类化,EasyDBO中创建jdbc连接上下文就是这样做的:
public class JDBCContext{
private static Logger logger = Logger.getLogger(JDBCContext.class);
private DataSource ds;
protected Connection connection;
private boolean isValid = true;
private static ThreadLocal jdbcContext;
private JDBCContext(DataSource ds){
this.ds = ds;
createConnection();
}
public static JDBCContext getJdbcContext(javax.sql.DataSource ds)
{
if(jdbcContext==null)jdbcContext=new JDBCContextThreadLocal(ds);
JDBCContext context = (JDBCContext) jdbcContext.get();
if (context == null) {
context = new JDBCContext(ds);
}
return context;
}
private static class JDBCContextThreadLocal extends ThreadLocal {
public javax.sql.DataSource ds;
public JDBCContextThreadLocal(javax.sql.DataSource ds)
{
this.ds=ds;
}
protected synchronized Object initialValue() {
return new JDBCContext(ds);
}
}
}
使用单例模式,不同的线程调用getJdbcContext()获得自己的jdbcContext,都是通过JDBCContextThreadLocal 内置子类来获得JDBCContext对象的线程局部变量
本文转载:http://blog.csdn.net/wenzhihui_2010/article/details/8985575
java jdbc深入理解(connection与threadlocal与数据库连接池和事务实)的更多相关文章
- 【Java EE 学习 16 上】【dbcp数据库连接池】【c3p0数据库连接池】
一.回顾之前使用的动态代理的方式实现的数据库连接池: 代码: package day16.utils; import java.io.IOException; import java.lang.ref ...
- JDBC 学习笔记(三)—— 数据源(数据库连接池):DBCP数据源、C3P0 数据源以及自定义数据源技术
本文目录: 1.应用程序直接获取连接的缺点(图解) 2.使用数据库连接池优化程序性能(图解) 3.可扩展增强某个类方法的功能的三种方式 4.自定 ...
- JDBC 学习复习6 学习与编写数据库连接池
之前的工具类DBUtil暴露的问题 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的 ...
- java web中jsp连接mysql数据库 以及数据库连接池的使用
将mysql-connector-java-5.1.6-bin.jar导入到tomcat的lib目录下. 在java项目中,只需要引入mysql-connector-java-5.1.6-bin.ja ...
- Java六大必须理解的问题
Java六大必须理解的问题 对于这个系列里的问题,每个学Java的人都应该搞懂.当然,如果只是学Java玩玩就无所谓了.如果你认为自己已经超越初学者了,却不很懂这些问题,请将你自己重归初学者行列.内容 ...
- Java Web(九) JDBC及数据库连接池及DBCP,c3p0,dbutils的使用
DBCP.C3P0.DBUtils的jar包和配置文件(百度云盘):点我下载 JDBC JDBC(Java 数据库连接,Java Database Connectify)是标准的Java访问数据库的A ...
- JDBC编程学习笔记之数据库连接池的实现
在JDBC编程的时候,获取到一个数据库连接资源是很宝贵的,倘若数据库访问量超大,而数据库连接资源又没能得到及时的释放,就会导致系统的崩溃甚至宕机.造成的损失将会是巨大的.再看有了数据库连接池的JDBC ...
- java web学习总结(十六) -------------------数据库连接池
一.应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大 ...
- [数据库连接池二]Java数据库连接池--C3P0和JDNI.
前言:上一篇文章中讲了DBCP的用法以及实现原理, 这一篇再来说下C3P0和JDNI的用法. 1.1.C3P0数据源 C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规 ...
随机推荐
- Jekens 配置多项目SCM GitLab+Jenkins持续集成环境
参考: 搭建GitLab+Jenkins持续集成环境图文教程 https://blog.csdn.net/ruangong1203/article/details/73065410 Jenkins中配 ...
- ArchLinux升级后deadbeef无法正常启动的解决办法
deadbeef是一款简约而不简单的音乐播放器, 占资源少, 支持的格式却不少. 昨天对ArchLinux进行了一次全面升级, 经历种种惊险, 终于跨越了从 glibc-2.16 到 glibc-2. ...
- angular4 get,post请求(带参数,与不带参数)
一:在app.module.ts引入HttpMoudle import { BrowserModule } from '@angular/platform-browser'; import { Htt ...
- UVA1386 【Cellular Automaton】题解
题面:UVA1386 Cellular Automaton 矩阵乘法+快速幂解法: 这是一个比较裸的有点复杂需要优化的矩乘快速幂,所以推荐大家先做一下下列洛谷题目练练手: (会了,差不多就是多倍经验题 ...
- bootstrap_bootstrap中日历范围选择插件daterangepicker的使用
1.引入脚本 <link rel="stylesheet" type="text/css" href="assets/css/bootstrap ...
- 免費域名申請.me .im .in .co .la .do .ms .kz .tk .ru .mu .pn .tt
免費申請域名 .la .la 域名 – 原先是ICANN分配給老撾的國家頂級域名,不過後來被同時作為了美國洛杉矶市的域名後綴. 免費申請地址: http://www.idv.la http://www ...
- MySQL 5.7以后怎么查看索引使用情况?
MySQL 5.7以后怎么查看索引使用情况? 0.在sys库中查看没用的索引 root@localhost [sys]>select * from schema_unused_indexes; ...
- 如何提高单片机Flash的擦写次数
所谓提高flash的擦写次数,并不是真正的提高flash擦写次数,而是通过以"空间换时间"概念,在软件上实现“操作的次数大于其寿命”.详见链接: http://bbs.eeworl ...
- 【Educational Codeforces Round28】
咸鱼选手发现自己很久不做cf了,晚节不保. A.Curriculum Vitae 枚举一下间断点的位置. #include<bits/stdc++.h> using namespace s ...
- Web测试技术要领
基于Web的系统测试与传统的软件测试既有相同之处,也有不同的地方,对软件测试提出了新的挑战.基于Web的系统测试不但需要检查和验证是否按照设计的要求运行,而且还要评价系统在不同用户的浏览器端的显示是否 ...