由于数据库连接,JMS连接是一种较为昂贵的资源,创建连接需要花不少时间,通常在连接创建后,会将连接缓存在连接池中,以便减少创建连接的时间和重复使用连接,节约资源和提高效率。关于连接池优点,介绍的文章较多,这儿就不再赘述了。下面主要通过示例代码来讨论如何实现一个连接池,并找出其中存在的问题,在后面的系列中对其进行优化。
连接池,对于有用户来说,最关心的就是能从连接池中获取连接,并能在连接关闭时,自动将连接归还到连接池中。一个最简单的连接池接口抽象如下:
public interface ConnectionPool {
/**
* 获取连接
*/
public Connection getConnection();
/**
* 释放连接
*/
public void releaseConnection(Connection conn);
}
一个模拟测试的连接类如下:
public class Connection {
/**连接池*/
private final ConnectionPool pool;
public Connection(ConnectionPool pool) {
this.pool = pool;
}
/**
* 关闭连接,将连接返回到连接池中
*/
public void close() {
pool.releaseConnection(this);
}
//省略其他逻辑方法 ... ...
}
对于连接池的实现,除了能从中获取连接和将不用的连接归还到连接池中,还必须有调整连接池大小的功能,当连接池中的空闲连接不够使用时,连接池需要创建新的连接来满足请求连接的线程,如果创建的连接已经达到连接池所限定最大数目且仍然没有连接可用时,需要将请求连接的线程加入等待队列中等待,有连接归还时,按照先进先出的原则唤醒等待线程去获取连接。下面是连接池的实现代码(由于篇幅关系没有实现超时,连接池空闲时减少连接数目等功能):
import java.util.*;
public class ConnectionPoolImpl implements ConnectionPool {
/**空闲连接*/
private List<Connection> freeList = new ArrayList<Connection>();
/**等待线程队列,先进去出*/
private final LinkedList<Object> waitQueue = new LinkedList<Object>();
/**最小连接数*/
private int minSize = 0;
/**最大连接数*/
private int maxSize = 32;
/**连接池中的总连接数*/
private int totalSize = 0;
/**连接池调整大小*/
private int step = 1;
/**连接池是否已经初始化*/
private boolean initialized = false;
/**调试模式*/
private boolean debug = false;
/**
* 初始化池
*/
private synchronized void initPool() {
if (initialized) {
return;
}
initialized = true;
if (debug) debugPrint("Connection pool initialized!");
for (int i = 0; i < minSize; i++) {
Connection conn = new Connection(this);
freeList.add(conn);
++totalSize;
if (debug) {
debugPrint("Increase a connection, " +
"total connections =" + totalSize
+ ", free connections " + freeList.size());
}
}
}
/**
* 获取连接,如果当前没有连接可用,则加入等待队列
*/
public Connection getConnection() {
Connection result = null;
while (true) {//直到获取到一条连接为止
result = internalGetConnection();
if (result != null) {
if (debug) {
debugPrint("Thread " + Thread.currentThread().getName()
+ " aquired a connection, " +
"total connections =" + totalSize
+ ", free connections " + freeList.size());
}
break;
} else {
Object monitor = new Object();
if (debug) {
debugPrint("Thread " + Thread.currentThread().getName()
+ " wait for a connection.");
}
//没有获取到连接,将当前线程加入等待队列
synchronized (monitor) {
synchronized (waitQueue) {waitQueue.add(monitor);}
try {monitor.wait();} catch (InterruptedException ignore) {}
//唤醒后会继续回到while循环,尝试获取连接
}
if (debug) {
debugPrint("Thread " + Thread.currentThread().getName()
+ " wakeup.");
}
}
}
return result;
}
/**
* 获取连接,如果没有连接,则尝试增加连接池
*/
private synchronized Connection internalGetConnection() {
if (!initialized) {
initPool();
}
Connection result = null;
if (!freeList.isEmpty()) {
result = freeList.remove(0);
} else {
if (totalSize < maxSize) {
if (debug) {
debugPrint("Current pool is empty, " +
"try to increase connection pool.");
}
//当前创建的连接总数小于最大连接数,增加连接池
result = increasePool();
}
}
return result;
}
/**
* 增加连接池,同时将最后创建的连接返回给当前线程
*/
private Connection increasePool() {
int localStep = step;
if (totalSize + step > maxSize) {
localStep = maxSize - totalSize;
}
Connection result = null;
int lastIndex = localStep - 1;
for (int i = 0; i < localStep; i++) {
Connection conn = new Connection(this);
++totalSize;
if (i == lastIndex) {
result = conn;//最后创建的连接返回给当前线程使用
if (debug) {
debugPrint("Increate a connection, " +
"total connections =" + totalSize
+ ", free connections " + freeList.size());
}
} else {
freeList.add(conn);
if (debug) {
debugPrint("Increate a connection, "
+ "total connections =" + totalSize
+ ", free connections " + freeList.size());
}
//增加连接后唤醒等待线程
notifyWaitingThreads();
}
}
return result;
}
/**
* 唤醒等待的线程
*/
private void notifyWaitingThreads() {
Object waitMonitor = null;
synchronized (waitQueue) {
if (waitQueue.size() > 0) {
waitMonitor = waitQueue.removeFirst();
}
}
if (waitMonitor != null) {
synchronized (waitMonitor) {
waitMonitor.notify();
}
}
}
/**
* 释放连接,同时唤醒等待的线程
*/
public synchronized void releaseConnection(Connection conn) {
freeList.add(conn);
if (debug) {
debugPrint("Release a connection, "
+ "total connections =" + totalSize
+ ", free connections " + freeList.size());
}
notifyWaitingThreads();
}
private void debugPrint(String debugStr) {
System.out.println(debugStr);
}
//省略其他getter, setter ...
}
下面创建一个连接池,并使用较多的线程来从连接池中不断获取连接,并使用一段时间后关闭连接。经测试,如果每个线程一次只获取一条连接并在使用完后关闭连接将其归还到连接池中,上面的连接池实现已经能支持较多并发不会出现问题。测试代码如下:
import java.util.Random;
public class ConnectionPoolTest {
/**
* 模拟客户端线程不断获取,释放连接的情况
*/
private static class PoolClient implements Runnable {
private static int idGenerator = 0;
private final ConnectionPoolImpl pool;
private final String threadName;
private Random random = new Random();
public PoolClient(ConnectionPoolImpl pool) {
this.pool = pool;
threadName = "pool-client-" + (++idGenerator);
Thread.currentThread().setName(threadName);
}
public void run() {
for (int i = 0; i < 100; i++) {
Connection conn = pool.getConnection();
System.out.println("Thread " + threadName
+ " aquired a connection.");
int sleepTime = (3 + random.nextInt(20)) * 1000;
try {
Thread.sleep(sleepTime);
} catch (InterruptedException ignore) {
}
conn.close();
System.out.println("Thread " + threadName
+ " release a connection.");
}
}
}
public static void main(String[] args) throws Exception {
ConnectionPoolImpl pool = new ConnectionPoolImpl();
pool.setMaxSize(100);
pool.setMinSize(5);
pool.setDebug(true);
//测试连接池,模拟1000个线程不断获取释放连接的情况
for (int i = 0; i < 1000; i++) {
Thread thread = new Thread(new PoolClient(pool));
thread.start();
}
}
}
上面是一个简单的连接池的实现,该连接池已基本能满足连接管理的功能,能缓存连接,并在连接不够使用时,对请求连接的线程进行排队,如果有连接回到连接池中时,会按照先进先出的原则唤醒等待队列中的线程。但是却存在以下较明显的问题:
- 连接池的初始化效率较低,连接池initPool()方法占用了整个连接池的锁,如果同时有很多线程在连接池尚未初始化时并发的调用getConnection()来获取连接,那么所有的线程都得等待initPool()将连接池初始化完毕后才可能获取到连接,如果初始化的时间足够长,将导致很多线程需要等待较长时间不能工作。
- 连接池的调整效率低下,同连接池初始化initPool()方法一样,连接池调整increasePool()也占用了整个连接池的锁,所以在调整连接池过程中,其他线程无法获取连接和归还连接,如果连接池的调整时间足够长,由于其他线程因为无法归还连接和获取连接导致连接池效率极其低下。
- 该连接池基本能满足每个线程从中取回一个连接,用完后就即使归还的情况。如果有部分线程需要取两个连接或者三个连接,用完后再一起归还,使用该连接池时,很明显会出现资源竞争饥饿死锁的情况。
在后面的系列中将讲解如何优化代码避免这些情况。
分享到:
相关推荐
本文将深入探讨OkHttp中的连接池实现,包括连接对象的添加、移除机制以及其工作原理。 首先,我们需要了解什么是连接池。连接池是一种资源管理技术,用于存储和管理预先建立的网络连接,避免每次请求都创建新的TCP...
Java Socket 连接池实现是提高网络应用性能和效率的关键技术之一。在高并发的网络环境中,频繁地创建和销毁Socket连接会导致大量的系统资源浪费,影响整体性能。为了解决这个问题,开发人员通常会使用连接池来管理和...
总的来说,Java连接池是提高数据库应用性能的重要工具,通过合理配置和选择合适的连接池实现,我们可以有效地管理和利用数据库连接,减少系统资源的消耗,提升整体系统的响应速度和稳定性。对于开发者来说,理解和...
在.NET Core 2.1框架下,可以使用.NET Standard库来实现高效、优化的数据库连接管理,特别是通过连接池来提高性能。本文将深入探讨如何在C#中使用MySQL数据库连接池。 首先,我们需要了解什么是数据库连接池。...
在Java中,可以使用Apache Commons Pool库来实现Socket连接池,或者自定义一个基于LinkedList或ConcurrentHashMap的数据结构来管理和维护连接。同时,可以结合JMX(Java Management Extensions)进行监控,查看连接...
在实际应用中,开发者通常会在Spring框架中配置数据源,选择合适的连接池实现,如在Spring Boot项目中,可以通过YAML或properties配置文件设置连接池的相关参数: ```yaml spring: datasource: type: ...
本话题将深入探讨如何在C++环境中利用连接池技术实现对Oracle数据库的高速连接与访问。 一、Oracle数据库基础 Oracle数据库提供了一套完整的数据管理解决方案,包括事务处理、数据仓库、网络数据库、安全性等。它的...
本资源提供的内容是关于ActiveMQ的连接池实现,分为两部分:一是作者自己实现的ActiveMQ连接池,二是新版本ActiveMQ自带的连接池。连接池是一种资源管理技术,通过复用已建立的数据库连接或网络连接,减少创建和销毁...
在IT领域,数据库连接池是一种优化...总的来说,Delphi实现数据库连接池是一项关键的技术,对于优化数据库访问效率和系统整体性能有着显著的影响。正确地设计和使用连接池,可以极大地提升应用程序的稳定性和用户体验。
1. **选择连接池实现**:首先,我们需要选择一个合适的数据库连接池实现,如Apache的DBCP、C3P0、HikariCP或Tomcat JDBC连接池等。这些连接池库提供了管理和维护数据库连接的功能。 2. **配置连接池**:在应用的...
总之,编码实现MQ连接池,是优化JMS消息发送性能的重要手段,它通过复用已建立的连接,减少了创建和销毁连接的开销,提高了系统效率。在实际应用中,应根据系统的负载和资源情况,合理配置连接池的大小,以达到最佳...
`ConnectionPool`可能是项目或类库的命名空间或类,包含实际的数据库连接池实现;`Demo`可能是一个示例应用程序,展示如何使用自定义的数据库连接池。 5. **连接池实现细节**: - **初始化**:在应用程序启动时,...
此外,本文还探讨了如何利用XML文件来存储和管理这些配置参数,以及如何在连接池初始化时加载这些优化后的参数,以此来实现连接池的自优化。 #### 二、自优化数据库连接池的关键要素 ##### 1. 动态调整机制 - **...
《连接池实现原理及效率测试》 连接池是数据库应用中的一个重要概念,它在系统设计中扮演着提高性能、优化资源利用的关键角色。本文将深入探讨连接池的实现原理,并通过实际测试分析其效率。 首先,我们需要理解...
总结来说,Kafka生产者连接池是通过复用连接资源来优化Kafka生产者性能的一种高效策略,结合Apache Commons Pool 2这样的对象池库和自定义的`kafkaPool-v1.0.jar`实现,可以提供更稳定、高效的Kafka生产环境。...
总结,Java 完整的数据库连接池实现涉及到选择合适的连接池实现、配置参数、初始化连接池以及在代码中正确地获取和释放连接。理解这些概念和步骤对于优化 Java 应用程序的数据库性能至关重要。通过合理配置和使用...
C3P0则是一个更复杂的连接池实现,它不仅提供连接池服务,还包含了连接测试和自动回收过期连接的功能。C3P0的配置更加灵活,可以通过XML或Java代码进行。 HikariCP是近年来颇受欢迎的连接池实现,以其高速度和低...
文章中的示例代码提供了一个简单的数据库连接池实现——`ConnectionPool`类。这个类包含了一些关键属性和方法,下面我们来逐一介绍: 1. **属性定义**: - `private String jdbcDriver=""`:存储数据库驱动名称。 ...
数据库连接池在Java中的实现是提高应用程序性能的关键技术之一,它通过复用已存在的数据库连接,避免了频繁创建和销毁连接导致的系统资源浪费。本文将深入探讨如何使用Java代码来实现一个简单的数据库连接池,并解释...