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;
}

2 番目の重要な問題は、明示的に制御していないクラスで同期していることです。サード パーティの 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

dataSourceまず、テストソースで動作しているかどうかを試してみてください。Apache Tomcat 6.0およびApache Tomcat 7.0
について は、Apache Tomcat JNDI Data Resource How To を参照することをお勧めします。

指示を注意深く見て、コードの何が問題なのかを分析してから、特定の問題で質問を更新してください。

于 2012-04-29T14:03:23.680 に答える
0

このコードは、マルチスレッド システムで接続リークを引き起こすことが事実上保証されています。closeDbConnection()プールから借用された最後の接続のみを閉じます。つまり、10 個のスレッドが呼び出されgetDbConnection()、その後が呼び出された場合closeDbConnection()、1 つの接続のみが閉じられ、9 個がまだ有効です。それを数回繰り返すと、プールが使い果たされます (接続が でクリーンアップされない限りfinalize()、おそらくそうではありません)。クラス全体を取り除くか、データソース ロケーターとしてのみ機能するように作り直しました。

于 2012-04-29T14:06:57.120 に答える
0

@arya、接続リークの問題があるようです。そのため、プールが使い果たされ、コードは新しい接続を取得するまで待機します。問題を分析するには、データベース監視ツールのいずれかを使用するか、手動でリークを追跡してみてください (接続を消費したが、使用後にプールに戻すのを忘れたコード内のポイント)。

于 2012-04-29T13:54:15.203 に答える