2016-11-27 40 views
1

我的下面的方法線程安全嗎?這個方法在Singleton類中。如何使併發哈希映射線程安全地使用get和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); 
    if(ps == null) { // If "ps" is already present in cache, then we don't have to synchronize and make threads wait. 
     synchronized { 
      ps = holder.get(cql); 
      if (ps == null) { 
      ps = session.prepare(cql); 
      holder.put(cql, ps); 
      } 
     } 
    } 
    return ps.bind(); 
    } 

我與卡桑德拉工作,並使用datastax Java驅動程序,所以我重用準備好的語句,這就是爲什麼我在這裏將其高速緩存。 Prepared StatementBoundStatement

有沒有更好的方式使我的getStatement方法線程安全(如果它是線程安全的),而不是像這樣使用同步塊?任何其他可能對這些操作都是線程安全的數據結構?我與Java 7

+0

啊,對不起,錯過了。 –

+0

您的問題的標題有點偏離。您不必使ConcurrentHashMap線程安全:它已經是線程安全的開箱即用。你想在這裏做的是讓你的_own_代碼線程安全。 –

+0

是的,你是對的我猜。 – john

回答

1

由於.putIfAbsent是Java7的工作,你可以使用它:

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

    public BoundStatement getStatement(String cql) { 
    Session session = TestUtils.getInstance().getSession(); 
    PreparedStatement ps = holder.get(cql); 
    if(ps == null) { // If "ps" is already present in cache, then we don't have to synchronize and make threads wait. 

     if (holder.putIfAbsent(cql, session.prepare(cql)) != null) { 
      // Someone else got there before, handle 
     } 
    } 
    return ps.bind(); 
    } 

注意的putIfAbsent仍然使用相同的同步內部。

+0

我應該在內部如果塊做什麼?如何處理這個和什麼? – john

+0

內部塊意味着你有兩個同時準備好的cql,只有一個在地圖上。如果你不在乎,就放下這個塊。如果你這樣做,記錄它,或拋出一個錯誤。 –

+1

注意:'putIfAbsent'存在於'ConcurrentHashMap'中,而不是'Map'。您需要更改字段聲明。 –

1

如果您正在尋找某種形式的memoization,那麼這是您在Java 7中可以完成的最佳/最簡單的操作。Guava有一個可以使用的計算Cache實現,Java 8在Map接口中有一個computeIfAbsent方法,但你顯然在這裏運氣不好。

如果你可以在空的競賽中創建對象,就像阿列克謝的回答所暗示的那樣,那將是最好的解決方案。如果你不能,你的實現既是線程安全又合理的。

這是一種雙重檢查鎖定的形式,但是,通過此實施方式,您可以確保在使用CHM的putget方法進行排序之前發生這種情況。