3

我在 Tomcat 中使用 JDBC 连接池。为了检索连接,我定义了一个连接工厂,如下所示:

public class ConnectionManager {

   // reference to the ConnectionManager
   private static ConnectionManager instance = null;
   // Connection to MySQL database
   private Connection connect = null;

   private static DataSource ds = null;
   // Logger
   public static final Logger logger = Logger
         .getLogger(ConnectionManager.class);

   static {

      try {
         Context initCtx = new InitialContext();
         Context envCtx = (Context) initCtx.lookup("java:comp/env");
         ds = (DataSource) envCtx.lookup("jdbc/ConnectionManager");
      } catch (NamingException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }
   }

   /**
    * Private Constructor .. since its a singleton
    * 
    */
   private ConnectionManager() {

   }

   public static ConnectionManager getInstance() {
      if (instance == null) {
         instance = new ConnectionManager();
      }
      return instance;
   }

   public Connection getDbConnection() {
      Connection conn = null;

      try {
         synchronized (DataSource.class) {

            conn = ds.getConnection();
         }
      } catch (SQLException e) {
         // TODO Auto-generated catch block
         e.printStackTrace();
      }

      return conn;
   }

   public void closeDbConnection() throws SQLException {
      conn.close();
   }
}

现在我看到我的代码总是卡在conn = ds.getConnection();一行。请让我知道我做错了什么。从我的 DAO 方法中,我使用以下方法来获取连接:conn = ds.getConnection();

显然这是一个多线程问题。我该怎么办?

4

4 回答 4

3

您的大部分课程似乎都围绕检索 JNDI 数据源并使用它来创建连接。不一定是个坏主意,但在这种情况下,您在程序中引入了一些错误,增加了复杂性。

首先,您的单身人士不是单身人士。您没有同步该getInstance方法,因此多个线程可以同时调用此方法。Java 中实现单例的最佳方法(不幸的是)是通过枚举:

public enum ConnectionManager {
    INSTANCE;
}

您的第二个重要问题是您正在同步一个您没有明确控制的类。没有什么可以阻止第三方 JAR 甚至您自己的应用程序中的其他类在 DataSource 类上同步,这使其成为死锁问题的普遍目标。我会从类中取出所有多余的方法并删除同步块:

public enum ConnectionManager {
    INSTANCE;

    private DataSource ds = null;

    ConnectionManager() {
      try {
         final Context initCtx = new InitialContext();
         final Context envCtx = (Context) initCtx.lookup("java:comp/env");
         ds = (DataSource) envCtx.lookup("jdbc/ConnectionManager");
      } catch (NamingException e) {
         e.printStackTrace();
      }
    }

   public Connection getConnection() throws SQLException {
      if(ds == null) return null;

      return ds.getConnection();
   }
}

现在,根据我的经验,大多数数据源实现都是线程安全的,所以上面的代码大部分时间都应该工作。但是,我们不应该依赖我们无法控制的实现,所以让我们在代码中添加一个安全同步,如下所示:

public enum ConnectionManager {
    INSTANCE;

    private DataSource ds = null;
    private Lock connectionLock = new ReentrantLock();

    ConnectionManager() {
      try {
         final Context initCtx = new InitialContext();
         final Context envCtx = (Context) initCtx.lookup("java:comp/env");
         ds = (DataSource) envCtx.lookup("jdbc/ConnectionManager");
      } catch (NamingException e) {
         e.printStackTrace();
      }
    }

   public Connection getConnection() throws SQLException {
      if(ds == null) return null;

      Connection conn = null;
      connectionLock.lock();
      try {
          conn = ds.getConnection();
      } finally {
          connectionLock.unlock();
      }

      return conn;
   }
}

您不必添加包装方法来关闭连接,这是调用代码的责任。祝你好运。

于 2012-04-29T14:44:38.430 回答
0

@arya,您似乎遇到了连接泄漏的问题,因此池已用尽,代码只是等到它获得新连接,要分析问题,请使用任何数据库监控工具,或手动尝试跟踪泄漏(代码中您使用了连接但忘记在使用后将其返回到池中的点)。

于 2012-04-29T13:54:15.203 回答
0

好吧,我会说首先尝试您dataSource是否使用测试源。
我建议查看 Apache Tomcat JNDI Data Resource How To,用于Apache Tomcat 6.0Apache Tomcat 7.0

仔细查看说明并分析代码中出了什么问题,然后用具体问题更新您的问题。

于 2012-04-29T14:03:23.680 回答
0

该代码几乎可以保证在多线程系统中导致连接泄漏。closeDbConnection()仅关闭从池中借用的最后一个连接 - 因此,如果调用了 10 个线程getDbConnection(),之后closeDbConnection(),只有 1 个连接被关闭,9 个仍然活着。重复几次并且池已用尽(除非在 中清理连接finalize(),但情况可能并非如此)。我会摆脱整个班级,或者将其重新设计为仅充当数据源定位器。

于 2012-04-29T14:06:57.120 回答