单例模式,它用以确保一个特定的类只有一个对象被实例化。它包含两种类型,有些书上叫singleton模式和Double—Checked Locking模式。
单例模式注意问题:
1、拥有一个特定的方法,这个方法被用于实例化需要的对象。当该方法被调用的时候,它检查这个对象是否被实例化。如果已实例化,这个方法仅仅返回这个对象的一个引用。如果对象未被实例化,这个方法将对象实例化并返回这个新的实例的引用。
2、类的构造函数定义为protected或private。
单例模式的格式
(1)懒汉式
public class Singleton{ //声明一个静态类的变量指向null private static Singleton instance = null; //将构造函数设为private private Singleton(); //设计静态方法,返回类的实例 public static synchronized Singleton getInstance(){ if(instance==null){ instance = new Singleton(); return instance; } } }
(2)饿汉式
饿汉式是线程安全的,在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变 。
懒汉式优点是延时加载、 是在需要的时候才创建对象。缺点是应该用同步。如果在创建实例对象时不加上synchronized则会导致对对象的访问不是线程安全的。
public class Singleton{ private static Singleton singleton = new Singleton(); private Singleton (){ } public Singleton getInstance(){ return singletion; } }
典型应用:
数据库连接池管理类的应用
1、数据库连接池管理类
package com.cvicse.util; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; import java.util.Properties; import java.util.StringTokenizer; import java.util.Vector; /** * 数据库连接池管理类 * * @功能 :管理类DBConnectionManager支持对一个或多个由属性文件定义的数据库连接池的访问. * 客户程序可以调用getInstance()方法访问本类的唯一实例 * @创建人 gao_jie * @创建日期 Jun 18, 2009 * @版本 1.0 * */ public class DBConnectionManager { private static DBConnectionManager instance; // 唯一实例 private static int clients; // 连接的客户端 private Vector drivers = new Vector();// 驱动集合 private Hashtable pools = new Hashtable();// 连接池 private Properties dbProps;// 属性文件 private PrintWriter log; // 日志变量 /** * 单例模式建构私有函数以防止其它对象创建本类实例 */ private DBConnectionManager() { this.init(); } /** * 采用单例模式,返回唯一实例.如果是第一次调用此方法,则创建实例 * * @return DBConnectionManager 唯一实例 */ public static synchronized DBConnectionManager getInstance() { if (instance == null) { instance = new DBConnectionManager(); } clients++; return instance; } /** * 获得一个可用的(空闲的)连接.如果没有可用连接,且已有连接数小于最大连接数 限制,则创建并返回新连接 * * @param name * 在属性文件中定义的连接池名字 * @return Connection 可用连接或null */ public Connection getConnection(String name) { DBConnectionPool dbPool = (DBConnectionPool) pools.get(name); if (dbPool != null) { return dbPool.getConnection(); } return null; } /** * 获得一个可用连接.若没有可用连接,且已有连接数小于最大连接数限制, 则创建并返回新连接. 否则,在指定的时间内等待其它线程释放连接. * * @param name * 连接池名字 * @param time * 以毫秒计的等待时间 * @return Connection 可用连接或null */ public Connection getConnection(String name, long time) { DBConnectionPool dbPool = (DBConnectionPool) pools.get(name); if (dbPool != null) { return dbPool.getConnection(time); } return null; } /** * 将连接对象返回给由名字指定的连接池 * * @param name * 在属性文件中定义的连接池名字 * @param con * 连接对象 */ public void freeConnection(String name, Connection con) { DBConnectionPool dbPool = (DBConnectionPool) pools.get(name); if (dbPool != null) { dbPool.freeConnection(con); } } /** * 关闭所有连接,撤销驱动程序的注册 */ public synchronized void release() { // 等待直到最后一个客户程序调用 if (--clients != 0) { return; } Enumeration allPools = pools.elements(); while (allPools.hasMoreElements()) { DBConnectionPool pool = (DBConnectionPool) allPools.nextElement(); pool.release(); } Enumeration allDrivers = drivers.elements(); while (allDrivers.hasMoreElements()) { Driver driver = (Driver) allDrivers.nextElement(); try { DriverManager.deregisterDriver(driver); log("撤销JDBC驱动程序 " + driver.getClass().getName() + "的注册"); } catch (SQLException e) { log(e, "无法撤销下列JDBC驱动程序的注册: " + driver.getClass().getName()); } } } /** * 读取属性完成初始化 */ private void init() { // 文件流输入方式 InputStream fileinputstream = null; try { fileinputstream = new FileInputStream("./src/db.properties"); } catch (FileNotFoundException e) { e.printStackTrace(); } try { dbProps = new Properties(); dbProps.load(fileinputstream); } catch (Exception e) { e.printStackTrace(); System.err.println("不能读取属性文件. " + "请确保db.properties在CLASSPATH指定的路径中"); return; } String logFile = dbProps.getProperty("logfile", "DBConnectionManager.log"); try { log = new PrintWriter(new FileWriter(logFile, true), true); } catch (IOException e) { System.err.println("无法打开日志文件: " + logFile); log = new PrintWriter(System.err); } // 加载驱动 loadDrivers(dbProps); // 创建连接池 createPools(dbProps); } /** * 装载和注册所有JDBC驱动程序 * * @param props * 属性 */ private void loadDrivers(Properties props) { String driverClasses = props.getProperty("drivers"); StringTokenizer st = new StringTokenizer(driverClasses); while (st.hasMoreElements()) { String driverClassName = st.nextToken().trim(); try { Driver driver = (Driver) Class.forName(driverClassName) .newInstance(); DriverManager.registerDriver(driver); drivers.addElement(driver); log("成功注册JDBC驱动程序" + driverClassName); } catch (Exception e) { log("无法注册JDBC驱动程序: " + driverClassName + ", 错误: " + e); } } } /** * 根据指定属性创建连接池实例. * * @param props * 连接池属性 */ private void createPools(Properties props) { Enumeration propNames = props.propertyNames(); while (propNames.hasMoreElements()) { String name = (String) propNames.nextElement(); if (name.endsWith(".url")) { String poolName = name.substring(0, name.lastIndexOf(".")); System.out.println(" poolName ||" + poolName + "|"); String url = props.getProperty(poolName + ".url"); if (url == null) { log("没有为连接池" + poolName + "指定URL"); continue; } String user = props.getProperty(poolName + ".user"); String password = props.getProperty(poolName + ".password"); String maxconn = props.getProperty(poolName + ".maxconn", "0"); int max; try { max = Integer.valueOf(maxconn).intValue(); } catch (NumberFormatException e) { log("错误的最大连接数限制: " + maxconn + " .连接池: " + poolName); max = 0; } DBConnectionPool pool = new DBConnectionPool(poolName, url, user, password, max); pools.put(poolName, pool); log("成功创建连接池" + poolName); } } } /** * 将文本信息写入日志文件 */ private void log(String msg) { log.println(new Date() + ": " + msg); } /** * 将文本信息与异常写入日志文件 */ private void log(Throwable e, String msg) { log.println(new Date() + ": " + msg); e.printStackTrace(log); } /*************************************************************************** ************************数据库连接池内部类************************************ **************************************************************************/ /** * * @功能:数据库连接池内类 此内部类定义了一个连接池.它能够根据要求创建新连接,直到预定的最大连接数为止. * 在返回连接给客户程序之前,它能够验证连接的有效性. * @创建人 gao_jie * @创建日期 Jun 19, 2009 * @版本 1.0 * */ class DBConnectionPool { private String poolName; // 连接池名字 private String dbConnUrl; // 数据库的JDBC URL private String dbUserName; // 数据库账号或null private String dbPassWord; // 数据库账号密码或null private int maxConn; // 此连接池允许建立的最大连接数 private int checkedOut; // 当前连接数 private Vector<Connection> freeConnections; // 保存所有可用连接 /** * 创建新的连接池构造函数 * * @param poolName * 连接池名字 * @param dbConnUrl * 数据库的JDBC URL * @param dbUserName * 数据库帐号或 null * @param dbPassWord * 密码或 null * @param maxConn * 此连接池允许建立的最大连接数 */ public DBConnectionPool(String poolName, String dbConnUrl, String dbUserName, String dbPassWord, int maxConn) { this.poolName = poolName; this.dbConnUrl = dbConnUrl; this.dbUserName = dbUserName; this.dbPassWord = dbPassWord; this.maxConn = maxConn; this.freeConnections = new Vector<Connection>(); } /** * 从连接池获得一个可用连接.如果没有空闲的连接且当前连接数小于最大连接 数限制,则创建新连接. * 如原来登记为可用的连接不再有效,则从向量删除之,然后递归调用自己以尝试新的可用连接. */ public synchronized Connection getConnection() { Connection conn = null;// 定义连接标量 if (freeConnections != null && freeConnections.size() > 0) { // 获取向量中第一个可用连接 conn = (Connection) freeConnections.firstElement(); freeConnections.removeElementAt(0); try { if (conn.isClosed()) { log("从连接池" + poolName + "删除一个无效连接"); // 递归调用自己,尝试再次获取可用连接 conn = getConnection(); } } catch (SQLException e) { log("从连接池" + poolName + "删除一个无效连接"); // 递归调用自己,尝试再次获取可用连接 conn = getConnection(); } } else if (maxConn == 0 || checkedOut < maxConn) { conn = newConnection(); } if (conn != null) { checkedOut++; } return conn; } /** * 从连接池获取可用连接.可以指定客户程序能够等待的最长时间 参见前一个getConnection()方法. * * @param timeout * 以毫秒计的等待时间限制 */ public synchronized Connection getConnection(long timeout) { long startTime = System.currentTimeMillis(); Connection conn = null;// 定义连接标量 while ((conn = getConnection()) == null) { try { wait(timeout); } catch (InterruptedException e) { e.printStackTrace(); } if ((System.currentTimeMillis() - startTime) >= timeout) { // wait()返回的原因是超时 return null; } } return conn; } /** * 创建新的连接 * * @return 返回数据库连接 */ private Connection newConnection() { Connection conn = null;// 定义连接标量 try { if (dbUserName == null) { conn = DriverManager.getConnection(dbConnUrl); } else { conn = DriverManager.getConnection(dbConnUrl, dbUserName, dbPassWord); } log("连接池" + poolName + "创建一个新的连接"); } catch (SQLException e) { log(e, "无法创建下列URL的连接: " + dbConnUrl); return null; } return conn; } /** * 将不再使用的连接返回给连接池 * * @param con * 客户程序释放的连接 */ public synchronized void freeConnection(Connection conn) { // 将指定连接加入到向量末尾 freeConnections.addElement(conn); checkedOut--; notifyAll(); // 删除等待队列中的所有线程 } /** * 关闭所有连接 */ public synchronized void release() { Enumeration<Connection> allConnections = freeConnections.elements(); while (allConnections.hasMoreElements()) { Connection con = (Connection) allConnections.nextElement(); try { con.close(); log("关闭连接池" + poolName + "中的一个连接"); } catch (SQLException e) { log(e, "无法关闭连接池" + poolName + "中的连接"); } } freeConnections.removeAllElements(); } } }
2、测试类
/* * * InforGuard Copyright 2008 CVICSE, Co.ltd . * All rights reserved. * * Package: com.cvicse.util * FileName: Test.java * */ package com.cvicse.util; import java.sql.Connection; /** * * @功能 * @创建人 gao_jie * @创建日期 Jun 19, 2009 * @版本 1.0 * */ public class Test { /** * @param args */ public static void main(String[] args) { // 文件流输入方式 DBConnectionManager connectionManager = DBConnectionManager .getInstance(); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Connection conn1 = connectionManager.getConnection("mysql"); Connection conn2 = connectionManager.getConnection("mysql"); Connection conn3 = connectionManager.getConnection("mysql"); Connection conn4 = connectionManager.getConnection("mysql"); Connection conn5 = connectionManager.getConnection("mysql"); System.out.println(" conn1 == " + conn1); System.out.println(" conn2 == " + conn2); System.out.println(" conn3 == " + conn3); System.out.println(" conn4 == " + conn4); System.out.println(" conn5 == " + conn5); connectionManager.freeConnection("mysql", conn1); connectionManager.freeConnection("mysql", conn2); connectionManager.freeConnection("mysql", conn3); connectionManager.freeConnection("mysql", conn4); connectionManager.freeConnection("mysql", conn5); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Connection conn6 = connectionManager.getConnection("mysql"); Connection conn7 = connectionManager.getConnection("mysql"); Connection conn8 = connectionManager.getConnection("mysql"); Connection conn9 = connectionManager.getConnection("mysql"); Connection conn10 = connectionManager.getConnection("mysql"); System.out.println(" conn6 == " + conn6); System.out.println(" conn7 == " + conn7); System.out.println(" conn8 == " + conn8); System.out.println(" conn9 == " + conn9); System.out.println(" conn10 == " + conn10); } }
3、测试结果
conn1 == com.mysql.jdbc.Connection@1f5d386
conn2 == com.mysql.jdbc.Connection@121f1d
conn3 == com.mysql.jdbc.Connection@1b8e059
conn4 == com.mysql.jdbc.Connection@910040
conn5 == com.mysql.jdbc.Connection@1a786c3
conn6 == com.mysql.jdbc.Connection@1f5d386
conn7 == com.mysql.jdbc.Connection@121f1d
conn8 == com.mysql.jdbc.Connection@1b8e059
conn9 == com.mysql.jdbc.Connection@910040
conn10 == com.mysql.jdbc.Connection@1a786c3
启动五个线程,释放后再启动仍然是原先的五个线程。
4、实际应用中的配置
上面所实现的连接池在程序开发时如何应用到系统中呢?下面以Servlet为例说明连接池的使用。
Servlet的生命周期是:在开始建立servlet时,调用其初始化(init)方法。之后每个用户请求都导致一个调用前面建立的实例的service方法的线程。最后,当服务器决定卸载一个servlet时,它首先调用该servlet的 destroy方法。
根据servlet的特点,我们可以在初始化函数中生成连接池管理类的唯一实例(其中包括创建一个或多个连接池)。如:
public void init() throws ServletException { connMgr = DBConnectionManager.getInstance(); } |
然后就可以在service方法中通过连接池名称使用连接池,执行数据库操作。最后在destroy方法中释放占用的系统资源,如:
public void destroy() { connMgr.release(); super.destroy(); } |
相关推荐
为了有效地解决这些问题,可以采用设计模式中的单例模式来管理数据库连接。 #### 单例模式简介 单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。这种模式通常用于那些需要...
总的来说,这个资源为Qt开发者提供了一种有效管理MySQL数据库连接的方法,结合了C++的单例模式和数据库连接池的概念,降低了开发复杂性,提升了程序的运行效率。在实际项目中,根据具体需求进行适当的调整和优化,将...
在该模式下,整个应用程序仅创建一个数据库连接池实例,所有数据库操作共享这个实例中的连接资源。这种设计方式能够有效地减少数据库连接的创建和销毁次数,从而提升整体性能。 #### 核心概念与实现细节 1. **单...
我们采用的策略是:设计一个符合单例模式的连接池管理类,在连接池管理类的唯一实例被创建时读取一个资源文件,其中资源文件中存放着多个数据库的 url 地址()、用户名()、密码()等信息。 3. 连接池的配置:...
解决多数据库服务器和多用户问题可以设计一个符合单例模式的连接池管理类,在连接池管理类的唯一实例被创建时读取一个资源文件,其中包含了不同的数据库连接信息。 Java JDBC 数据库连接池的优点包括: 1. 提高...
在数据库连接池的场景下,单例模式尤其适用,因为整个应用程序只需要一个连接池实例,确保所有数据库操作都共享同一组连接,避免资源浪费。 接下来,我们讨论通用的BaseDao类。BaseDao通常是一个抽象基类,包含了...
以下是一个简单的Java单例数据库连接池示例: ```java public class DatabaseConnectionPool { private static final DatabaseConnectionPool instance = new DatabaseConnectionPool(); private DataSource ...
Java单例模式是一种设计模式,它保证一个类...总的来说,这个Java单例模式的数据库连接源码提供了一种简单但有效的数据库连接管理方式,适合小规模应用。然而,在大型项目中,建议采用连接池来管理和优化数据库连接。
这段代码定义了一个名为`GetConn`的类,该类实现了数据库连接池的功能,并采用了单例模式。 1. **类定义**: ```java public class GetConn { // ... } ``` `GetConn`类封装了数据库连接池的操作逻辑。 2. *...
在本项目中,可能会应用到工厂模式(用于创建数据库连接)、单例模式(确保数据库连接池在整个应用中只有一个实例)和代理模式(为数据库连接提供一个接口,便于管理和监控)等。 数据库连接池的核心是管理数据库...
数据库连接池是应用程序管理数据库连接的一种机制,它提高了数据库访问的效率和资源利用率。通过复用已建立的数据库连接,避免了频繁创建和关闭连接所消耗的时间和系统资源。连接池的基本思想是预先创建一定数量的...
数据库连接池** 数据库连接是消耗系统资源较大的操作之一。为了提高效率并减少开销,通常采用连接池技术。在连接池的设计中,单例模式的运用能够保证所有请求都通过同一个连接池来进行数据库操作,从而避免频繁...
2. **多数据库服务器和多用户支持**:对于需要同时连接到不同数据库服务器的应用程序来说,可以设计一个符合单例模式的连接池管理类,并在类实例化时读取一个资源文件,该文件包含了多个数据库的URL、用户名和密码等...
数据库连接池是一种存储和管理多个数据库连接的技术,这些连接可以被应用程序重复使用而无需每次都重新建立和销毁连接。连接池的主要目标是提高性能和资源利用率。 #### 三、连接池的工作原理 1. **连接池的初始化*...
数据库连接池是数据库管理中的一个重要概念,它有效地管理和复用数据库连接,以提高数据库操作的效率和系统的资源利用率。在Python中,dbutils库提供了一种便捷的方式来实现这个功能。dbutils库通常与其他的数据库...
这种设计遵循了软件设计模式中的“单例模式”,确保在整个应用程序生命周期中只有一个数据库连接池实例存在,从而保证了连接的统一管理和高效利用。 单例模式是一种常用的软件设计模式,它的主要目的是控制类的实例...
可以设计一个符合单例模式的连接池管理类,在连接池管理类的唯一实例被创建时读取一个资源文件,其中资源文件中存放着多个数据库的 url 地址、用户名、密码等信息。 * 事务处理:我们知道,事务具有原子性,此时...
在数据库连接池中应用单例模式,可以保证整个应用程序中只有一个数据库连接池实例,避免资源浪费和管理混乱。 3. 配置文件(db.properties):这是一种常见的方式来存储应用程序的配置信息,如数据库的URL、用户名...
在实际开发中,单例模式常常用于配置管理、缓存、日志、数据库连接池等场景。例如,Spring框架中的ApplicationContext就是通过单例模式来管理所有bean的。然而,需要注意的是,过度使用单例可能导致系统设计过于紧密...