2017-01-01 8 views
2

私はCassandraと協力してDatastax Javaドライバを使用しています。準備した文をキャッシュして再利用しようとしています。putIfAbsentで「get/check/put」の使用を置き換えます。

private static final Map<String, PreparedStatement> holder = new ConcurrentHashMap<>(); 

    public BoundStatement getStatement(String cql) { 
    Session session = TestUtils.getInstance().getSession(); 
    PreparedStatement ps = holder.get(cql); 
    // no statement is cached, create one and cache it now. 
    if (ps == null) { 
     synchronized (this) { 
     ps = holder.get(cql); 
     if (ps == null) { 
      ps = session.prepare(cql); 
      holder.put(cql, ps); 
     } 
     } 
    } 
    return ps.bind(); 
    } 

私はそれは、スレッドセーフであることを確認する必要がありますのでマイ上記getStatement方法は、複数のスレッドによって呼び出されます。私はJava 7を使用しているので、残念ながらcomputeIfAbsentを使用することはできません。

静的アナライズツールに対してコードを実行したとき、Java 7のコードを書く上でより良い方法があると思うようになりました。

Might be better to replace use of get/check/put with putIfAbsent 

更新:

public BoundStatement getStatement(String cql) { 
    Session session = TestUtils.getInstance().getSession(); 
    PreparedStatement ps = holder.get(cql); 
    // no statement is cached, create one and cache it now. 
    if (ps == null) { 
     ps = session.prepare(cql); 
     PreparedStatement old = holder.putIfAbsent(cql, ps); 
     if (old!=null) 
     ps=old; 
    } 
    return ps.bind(); 
    } 
+2

これは、いくつかの理由で間違っています。 1つは、インスタンスを同期させて静的フィールドを保護することです。もう1つは、同期とロックフリーのコレクションを混在させることです。 – shmosel

+0

あなたのアプローチでは、準備された文のインスタンスをキャッシュする価値があると仮定しています。それは...ですか?毎回新しいインスタンスを作成する実際のパフォーマンスヒットは何ですか? (本物の質問;私はそれらを扱うことはめったにないので、どれくらい重いか分かりません)。 –

+1

これらのプリペアドステートメントをキャッシュしないと、すべてのログに警告メッセージ '既に準備されたクエリを再準備しています。同じクエリを2回以上準備することは、一般的に反パターンであり、パフォーマンスに影響する可能性があることに注意してください。この文を一度だけ準備することを検討してください.' datastax java driverから。ここには[質問](http://stackoverflow.com/questions/22915840/re-using-preparedstatement-when-using-datastax-cassandra-driver)があります。そのことについてもっと詳しく述べていますので、私は準備文を再利用することにしました。 – john

答えて

1

これは、1つのスレッドが、彼らは同じプリペアドステートメントをしようとしていない場合でも、別のをブロックすることができることを除いて、あなたはそれを持ってあまりにも悪くはありません。

Java 8でcomputeIfAbsentを使用すると、実際にはるかに優れています。 Javaの7では、あなたはこれを行うことができます。

ps = holder.get(cql); 
if (ps == null) { 
    ps = session.prepare(cql); 
    PreparedStatement old = holder.putIfAbsent(cql, ps); 
    if (old!=null) 
    ps=old; 
} 

2つのスレッドが同時に同じものを作ってみる場合は、時折が、キャッシュを使用しないようにだけで同等だ最悪の場合には、不必要なPreparedStatementを行います。

あなたはグアバのライブラリを使用することができればまた、その後、グアバLoadingCacheが正確に何をしたいん:https://google.github.io/guava/releases/16.0/api/docs/com/google/common/cache/CacheBuilder.html

+0

私はまだ 'synchronized'ブロックが必要ですか?それを取り除き、あなたがputIfAbsentで示唆しているものを使うことができますか? – john

+0

同期ブロックは必要ありません –

+0

コードで質問を更新しました。あなたはそのような何かを意味しますか?ちょうど私がそれを正しいことを確かめること。 – john

関連する問題