2011-02-02 8 views
9

マルチスレッドJavaアプリケーションでは、すべてのスレッドが同期してdbに確実にアクセスできるようにするには、何が最善の解決策であるかを尋ねます。たとえば、各スレッドは個別のトランザクションを表し、最初にdbの値をチェックしてから、データベースの一部のフィールドを挿入または更新する必要があります(チェック、挿入、コミットのアプリケーション間では他の処理が行われます)。しかし問題は、別のスレッドが同じテーブルで同じことをしている可能性があることです。より具体的な例。スレッドT1はトランザクションを開始し、テーブルENTITY_TABLEをエントリ '111'でチェックし、見つかった場合は日付を更新し、見つからなければ新しいエントリを挿入してトランザクションをコミットします。今度はスレッドT2がまったく同じことをすると想像してください。現在、問題はほとんどありません。 1. T1とT2はdbをチェックして何も見つけず、両方とも同じエントリを挿入します。 2. T1はdbをチェックし、古い日付のエントリを検索しますが、コミットT2はすでにエントリを最新の日付に更新しています。 3.キャッシュを使用してキャッシュへのアクセスを同期化すると、問題が発生します.T1は、キャッシュに追加、ロック解除、コミットが見つからない場合、ロックチェックdbとキャッシュを取得します。 T2は同じことを行い、コミットしようとするキャッシュ内のエントリを見つけます。しかし、T1トランザクションは失敗し、ロールバックされます。現在、T2はENTITY_TABLEに挿入する必要がありますが、そのことは分かりません。 4.もっと?Javaの複数スレッドデータベースへのアクセス

私はsyncronizationと問題3を解決する単純なカスタムキャッシュを作成するために取り組んでいます。しかし、もっと興味深いことがありますか?誰もが同様の問題を解決しなければならなかったのですか?あなたはどうしましたか?

答えて

7

これは、主にDB内で、希望のtransaction isolationレベルを設定することによって処理する必要があります。次に、ロック戦略(optimisticまたは悲観的)を選択する必要があります。

トランザクション分離がなければ、Javaドメインのみでトランザクションの整合性を確保することは困難です。特にDBが現在Javaアプリケーションからのみアクセスされている場合でも、これは将来変更される可能性があります。

あなたの説明から選択する分離レベルは、シリアライズ可能な最も高い分離レベルが必要なように見えます。。しかし、実際には、これは広範なロックのために実際に性能が低下する傾向があります( )。したがって、特定の状況に最適な分離とパフォーマンスのバランスを見つけるために、要件を再評価することができます。

+0

感謝を。ことは、アプリケーションは非常に実行する必要があります、それは16または多分32スレッドで実行される可能性があります。そして、それぞれがトランザクションを開始し、多くの処理を行い、結果を取り込み、コミット・ステップで挿入と更新を行います。すべてがjdbcバッチで実行されます。その後、トランザクションがコミットされます。とにかく私の状況では、シリアライズ可能なレベルは32スレッドのためにあまりにも多くなります。私はこれをJavaアプリケーションのキャッシュで解決できることを願っています。 – nesvarbu

1

私はあなたがそれをクエリする前にテーブルをロックしなければならないと思います。これにより、スレッドの順次操作が強制されます。あなたのスレッドは、ロックを待つ必要があるという事実のために準備されなければなりません。もちろん、ロック取得はタイムアウトするかもしれません。これにより、アプリケーションにかなりのボトルネックが発生するばかりでなく、スレッドがすべてデータベース・リソース用にキューに入れなければならないこともあります。

1

あなたが直面している問題はです。

シリアル化可能な分離が必要なwhere句で、関連する行を各スレッドにロックさせる必要があるようです。

1

なぜあなたは車輪を再発明しようとしていますか?私はトランザクション(例えば、HibernateやEclipselinkのようなJPA仕様の実装者)を使ってデータベースアクセスのORマッパーフレームワークを使うことを提案します。トランザクションを処理するSpring DAOを追加することもできます。次に、ビジネスロジックに集中し、そのような低レベルのものを気にする必要はありません。

2

SQL SELECTをデータベースから選択し、後で同じ行を更新する場合は、Java開発者として2つの選択肢があります。ROWLOCK、または何 行ロックの構文を使用してSELECT

  1. はあなた 特定のデータベースのためです。

  2. は、行を選択して、あなたの処理を行い、 あなたは 更新する準備が整いました直前に、他のスレッドが変更を行った場合 を確認するために、再度行を選択します。 2つのSELECTSが同じ 値を返す場合は、UPDATE。そうでない場合は、 エラーを投げたり、処理をやり直してください。

2

Sqlliteデータベースを使用しているマルチスレッドのJavaプログラムで作業しているときに、この問題が発生しました。ファイルロックを使用するので、同時に1つのスレッドしか作業をしていないことを確認しなければなりませんでした。

私は基本的に同期を使用して終了しました。 ConnectionFactoryがdb接続を返すとき、接続を使用するときにロックするロックオブジェクトも返します。だから、手動で同期ロックを行う、またはあなたのためにそれを行い、それ以下のクラスのサブクラスを作成できます。

/** 
    * Subclass this class and implement the persistInTransaction method to perform 
    * an update to the database. 
    */ 
public abstract class DBOperationInTransaction { 

    protected Logger logger = Logger.getLogger(DBOperationInTransaction.class.getName()); 

    public DBOperationInTransaction(ConnectionFactory connectionFactory) { 
     DBConnection con = null; 

     try { 
      con = connectionFactory.getConnection(); 

      if(con == null) { 
       logger.log(Level.SEVERE, "Could not get db connection"); 
       throw new RuntimException("Could not get db connection"); 
      } 

      synchronized (con.activityLock) { 
       con.connection.setAutoCommit(false); 
       persistInTransaction(con.connection); 
       con.connection.commit(); 
      } 

     } catch (Exception e) { 
      logger.log(Level.SEVERE, "Failed to persist data:", e); 
      throw new RuntimeException(e); 
     } finally { 
      if(con != null) { 
       //Close con.connection silently. 
      } 
     } 
    } 

    /** 
     * Method for persisting data within a transaction. If any SQLExceptions 
     * occur they are logged and the transaction is rolled back. 
     * 
     * In the scope of the method there is a logger object available that any 
     * errors/warnings besides sqlException that you want to log. 
     * 
     * @param con 
     *   Connection ready for use, do not do any transaction handling 
     *   on this object. 
     * @throws SQLException 
     *    Any SQL exception that your code might throw. These errors 
     *    are logged. Any exception will rollback the transaction. 
     * 
     */ 
    abstract protected void persistInTransaction(Connection con) throws SQLException; 

} 

とのDBConnection構造体:答えを

final public class DBConnection { 
    public final Connection connection; 
    public final String activityLock; 

    public DBConnection(Connection connection, String activityLock) { 
     this.connection = connection; 
     this.activityLock = activityLock; 
    } 

} 
関連する問題