在整个地图上同步读取和写入的最好方法



获取json配置这是字符串到布尔值的映射(这是json模式)

我必须实现这个方法

private volatile Map<String, Boolean> flags = new HashMap<>();
private volatile String currFlagJson = "";
private final Retriever retriever; // This is initialized elsewhere, but it basically returns a cached json with a separate thread updating it. 
public Optional<Boolean> isFlagEnabled(final String featureFlag) {
final var json = retriever.retrieve();
if (!json.equals(currJson.get)) {
updateMap(json);
return Optional.ofNullable(flags.get(featureFlag));
} else {
return Optional.ofNullable(flags.get(featureFlag));
}
}
private synchronized updateMap(final String newJson) {
final var newMap = expensiveJsonParsingOperation(newJson)
flags = newMap;
currFlagJson = newJson;
}

我试图避免多个线程并发更新地图,但这我不认为完全工作。如果多个线程竞争并调用equals方法,而其中一个线程正在更新,那么最终所有线程将尝试竞争更新它。我当然可以同步整个isFlagEnabled,但这将是不必要的锁定,我可以服务读请求,而昂贵的jsonparsingoperation正在发生。

我正在想办法最好地……

  1. 确保只有一个线程在需要时更新此地图。
  2. 当这个更新发生时,不会阻塞线程的读取。

也许。https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html?或者其他我能用的东西?

您可以使用双重检查锁定来确保它只更新一次,而不会在读取器之间引入太多争用:

public Optional<Boolean> isFlagEnabled(final String featureFlag) {
var json = retriever.retrieve();
if (!json.equals(currFlagJson)) {
synchronized (this) {
if (!json.equals(currFlagJson)) {
flags = expensiveJsonParsingOperation(newJson);
currFlagJson = json;
}
}
}
return Optional.ofNullable(flags.get(featureFlag));
}

请注意,如果您只是更新引用而不修改映射本身,则不需要使用ConcurrentMap。如果使用不可变映射,还可以从flags中省略volatile(依赖于currFlagJson内存屏障来保持映射引用的新鲜)。否则,如果在另一个线程更新字段时读取字段,则存在部分发布的风险。无论哪种方式,currFlagJson必须是volatile,以确保它不被缓存。

最新更新