Кэш подготовлен в потоке безопасным способом? - PullRequest
0 голосов
/ 21 февраля 2020

Я кэширую подготовленное заявление, поэтому мне не нужно готовить его снова при работе с драйвером данных java (cassandra). Ниже приведен мой код, и он работает:

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

  public ResultSetFuture send(final String cql, final Object... values) {
    return executeWithSession(new SessionCallable<ResultSetFuture>() {
      @Override
      public ResultSetFuture executeWithSession(Session session) {
        BoundStatement bs = getStatement(cql, values);
        bs.setConsistencyLevel(consistencyLevel);
        return session.executeAsync(bs);
      }
    });
  }

  private BoundStatement getStatement(final String cql, final Object... values) {
    Session session = getSession();
    PreparedStatement ps = cache.get(cql);
    // no statement cached, create one and cache it now.
    // below line is causing thread safety issue..
    if (ps == null) {
      ps = session.prepare(cql);
      PreparedStatement old = cache.putIfAbsent(cql, ps);
      if (old != null)
        ps = old;
    }
    return ps.bind(values);
  }

Но Проблема в том, что метод send будет вызываться несколькими потоками, поэтому я подозреваю, что мой метод getStatement не является потокобезопасным из-за проверки if (ps == null). Как я могу сделать его безопасным для потоков?

Я хотел избежать использования ключевого слова synchronize, поэтому хотел посмотреть, есть ли лучший способ. Сейчас я работаю с Java 7.

1 Ответ

1 голос
/ 21 февраля 2020

Вместо этого вы можете использовать computeIfAbsent. Согласно документации это:

Если указанный ключ еще не связан со значением, пытается вычислить его значение, используя заданную функцию отображения, и вводит его в эту карту, если не имеет значение null. Весь вызов метода выполняется атомарно, поэтому функция применяется не более одного раза для каждой клавиши. Некоторые попытки обновления этой карты другими потоками могут быть заблокированы во время вычислений, поэтому вычисление должно быть коротким и простым и не должно пытаться обновить какие-либо другие сопоставления этой карты.

Код будет выглядеть следующим образом:

private BoundStatement getStatement(final String cql, final Object... values) {
   PreparedStatement pr = cache.computeIfAbsent(
        query,  key -> session.prepare(key));
   return pr.bind(values);
}

, хотя, поскольку подготовка может занять больше времени, это не рекомендуется ... Я бы вместо этого использовал явные блокировки ...

PS Обратите внимание, что Java драйвер 4.x имеет встроенный кэш , поэтому можно подготавливать один и тот же оператор несколько раз.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...