2012-02-01 13 views
2

大きなマルチスレッドアプリケーションで使用するためのデータベース接続プールを作成して、次のコードでjdk4標準をターゲットにして作成しました。テストケースでLAN経由でmysqlデータベースを0.4秒で1000回クエリできます。ここでこのロックの変更は、JDBCのパフォーマンスに大きな影響を与えるのはなぜですか?

synchronized(lockA) { 
    if(free.size() > 0) { 
     c = (Connection) free.removeFirst(); 
    } 

    if(c == null) { 
     c = DriverManager.getConnection(query, name, passw); 
    } 
} 

LOCKAは、リストが変更され、アクセスされた場合、それが使用される、フリーリスト(LinkedListの)を保護します。 getConnectionをこのロックから自分自身の保護ブロックに移動することは理にかなっていました。 getConnectionは、スレッドセーフではないため、ロックによって保護する必要があります。

私はそれを変更すると、DriverManagerとリストは別々のロックで保護されます。

synchronized(lockA) { 
    if(free.size() > 0) { 
     c = (Connection) free.removeFirst(); 
    } 
} 

if(c == null) { 
    synchronized(lockB) { 
     c = DriverManager.getConnection(query, name, passw); 
    } 
} 

私は連続したキャッシュミス(Cがヌル)になり、パフォーマンスが低下し、同じクエリを実行するのに0.4秒かかってしまいます。

これはなぜですか?

EDIT:

私はあまりにも多くの接続を作成するときに問題が途中から機能ブロックを生じ、これを解決しました。

これは、機能の開始時に起こったことです。

synchronized(waitLocK) { 
    try { 
     while(count >= limit) { 
      waitLock.wait(); 
     } 
    } catch (InterruptedException e) { 
    } 
} 

接続が解除されると、waitLockが解放されます。しかし、ここで起こっているのは、接続を作成するコードブロックの後で、カウント変数(volatile)がインクリメントされるということです。

これは、1000スレッドが待機テストを通過しようとしたときにカウントがまだ0だったためにすべて通過し、getConnection()がオーバーロードされたためです。

試行後にカウント++を移動すると、この問題が解決されます。

+0

'c'はグローバル変数ですか? – Tudor

+0

申し訳ありません。Cは、この関数のローカルです。 – FEiN

+3

「データベース接続プールを作成しています」....何のために?あなたが使用できるいくつかのオープンソースのものがあります。解決するのは難しい問題ですが、ホイールを再構築しないでください。 – skaffman

答えて

0

コードの2番目の形式では、無制限のスレッド数で同時にデータベースへの接続を要求できます。最初のスレッドでは、一度に1つのスレッドで新しい接続を要求できます。あなたのコードがそれに同期したキーワードを持つ

場合、それはおそらく間違っている、と微妙と予測不可能な方法で失敗します。

Javaで並行コードを書いて、私は従って、簡単なルールを持っています。

標準的な並行処理クラスで必要な同時実行結果を達成できない理由を冗長に考える必要があります。

skaffmanさんのコメントは正しいですか。実際には接続プールを実装したくありません。使用可能な実装のいずれかを選択します。

これは接続プールではないとしましょう。 ConcurrentLinkedQueueの代わりにsynchronized + LinkedListを使用する理由ロジックがリストから接続を取得しようとします。接続が利用できない場合は、新しい接続が作成されます(呼び出し元は、接続が完了したときにリストに戻っている可能性があります)。

Java Concurrency In Practiceは読んでいないと素晴らしい本です。

関連する問題