0

Ich arbeite mit Cassandra und Verwendung von Datastax Java-Treiber. Ich versuche, vorbereitete Anweisungen wiederzuverwenden, indem ich sie zwischenspeichere.Wie funktioniert "PutIfAbsent" in CHM?

private static final ConcurrentHashMap<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) { 
     holder.putIfAbsent(cql, session.prepare(cql)); 
    } 
    return ps.bind(); 
    } 

Prepared Statement und BoundStatement von DataStax Java-Treiber.

Diese getStatement Methode wird von mehreren Threads aufgerufen, so dass ich sicherstellen muss, dass es threadsicher ist. Ich arbeite mit Java 7.

Was wird putIfAbsent hier tun, wenn wir zwei gleiche cql vorbereitet Aussagen erhalten? Ist mein Code Thread sicher und es gibt keine Race Condition?

Update: -

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(); 
    } 
+0

Wie wäre es jetzt? Sieht es richtig aus? – john

+0

Ja, das sieht gut aus. – Holger

Antwort

1

Ihr Code hat eine Race-Bedingung, die zweimal in session.prepare(cql) dazu aufgerufen, zur Folge haben kann (oder mehr) für jeden cql Parameter. Die putIfAbsent bietet in diesem Fall keinen wirklichen Vorteil gegenüber einem normalen put.

Wenn Sie auf Java 8, könnten Sie dies effizient, ohne die Erstellung von Duplikaten schreiben mit

PreparedStatement ps = holder.computeIfAbsent(cql, key -> session.prepare(key)); 
+0

Wenn jemand einen großen Anwendungsfall für 'PutIfAbsent' hat, würde ich es gerne hören. – Kayaman

+0

Ich bin immer noch auf Java 7. Was kann ich tun, um dieses Problem mit Java 7 zu vermeiden? – john

+0

@david Um ehrlich zu sein, habe ich keine sehr elegante Lösung für Java 7. Sie müssten den "get/check for null/put" Teil manuell sperren. – Kayaman

1

Sie tun in der Tat eine Race-Bedingung, aber Ihr putIfAbsent ist wahrscheinlich immer noch etwas besser als reines put, wie es hält die alte Aussage, so dass es nie zwei Instanzen im echten Gebrauch gibt. Der Vorteil ist winzig, da er sich nur im Falle einer Wettlaufsituation manifestiert.

Es sieht aus wie eine Version von Double-checked locking. Legen Sie einfach einen synchronisierten Block um die Initialisierung und überprüfen Sie, ob es wirklich benötigt wird.

Ich bin eher skeptisch über die PreparedStatement Caching, da es veränderbar ist. Sie benötigen möglicherweise mehrere Instanzen für denselben cql. Dies ist auch machbar, aber Sie müssten sie richtig erwerben und freigeben.

+0

Meine Idee der Zwischenspeicherung der vorbereiteten Aussage kam von [this] (http://stackoverflow.com/questions/22915840/re-using-preparedstatement-when-using-datastax-cassandra-driver) sonst wird es immer wieder warnen. – john

+0

Ich habe die Frage mit dem synchronisierten Block aktualisiert. Das hast du gemeint? – john

+0

@david Ja, der synchronisierte Block sieht gut aus. In Bezug auf die Veränderlichkeit sieht [diese Antwort] (http://stackoverflow.com/a/22917891/581205) gut aus (ich weiß nichts über Cassandra). Übrigens, Ihr ursprünglicher Race-Zustand ist akzeptabel, da Sie nur die Effizienzwarnung erhalten. – maaartinus

Verwandte Themen