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规 ...
随机推荐
- Github遇到Permanently added the RSA host key for IP address '52.74.223.119' to the list of known hosts.
警告:为IP地址192.30.252.128的主机(RSA连接的)持久添加到hosts文件中,那就来添加吧! 解决办法: vim /etc/hosts 添加一行:52.74.223.119 githu ...
- c# net 使用反射为对象赋值
public T Bson2T(MongoDB.Bson.BsonDocument bson) { T t = default(T); //获取T类中的所有属性 PropertyInfo[] Tpro ...
- TIP协议
1. TIP是什么? CISCO给TIP的定义如下: The TIP protocol specifications describe how to multiplex multiple screen ...
- python最大最小距离算法贴近度评价法
1.大最小贴近度评价法 概念: 贴近度表示两个模糊几何之间的彼此接近程度,在模糊模式识别方法中采用贴近度的大小识别待判别模糊子集的模式类别.为衡量待识别子集的类别,需要判别各个阶段与标杆模糊集合之间的 ...
- 【译】第十五篇 Integration Services:SSIS参数
本篇文章是Integration Services系列的第十五篇,详细内容请参考原文. 简介在前一篇,我们使用SSDT-BI将第一个SSIS项目My_First_SSIS_Project升级/转换到S ...
- J - Clairewd’s message HDU - 4300(扩展kmp)
题目链接:https://cn.vjudge.net/contest/276379#problem/J 感觉讲的很好的一篇博客:https://subetter.com/articles/extend ...
- Oracle 11.2.0.4在线(Online mode)打补丁14084247解决WRH$_ACTIVE_SESSION_HISTORY不会自动切割的问题
安装了Oracle Database Release 11.2.0.4之后,发现WRH$_ACTIVE_SESSION_HISTORY始终不会自动分割删除, 后来才发现需要应用补丁:14084 ...
- int、long、long long取值范围
unsigned int 0-4294967295 int -2147483648-2147483647 unsigned long 0-4294967295 long -214748 ...
- RabbitMQ--Hello world!(一)
Introduction RabbitMQ is a message broker. The principal idea is pretty simple: it accepts and forwa ...
- 栈应用之 括号匹配问题(Python 版)
栈应用之 括号匹配问题(Python 版) 检查括号是否闭合 循序扫描被检查正文(一个字符)里的一个个字符 检查中跳过无关字符(所有非括号字符都与当前处理无关) 遇到开括号将其压入栈 遇到闭括号时弹出 ...