英文:
Best way to synchronize reads and writes on a map with potential updates on the entire map
问题
以下是代码的翻译部分:
// 定义了一个用于获取 JSON 配置的代码,这个 JSON 是一个字符串到布尔值的映射(它是一个保证的 JSON 模式)
// 我需要实现这个方法
private volatile Map<String, Boolean> flags = new HashMap<>();
private volatile String currFlagJson = "";
private final Retriever retriever; // 这个在其他地方初始化,但基本上返回一个缓存的 JSON,并使用单独的线程更新它。
public Optional<Boolean> isFlagEnabled(final String featureFlag) {
final var json = retriever.retrieve();
if (!json.equals(currFlagJson)) {
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 方法,而其中一个线程正在更新,那么最终所有线程都会尝试争夺更新它。当 expensiveJsonParsingOperation 正在进行时,我当然可以同步整个 isFlagEnabled,但这将是不必要的锁定,并且我可能正在提供读取请求。
我正在尝试找出如何最好地实现以下目标:
- 在需要时确保只有一个线程更新此映射。
- 不要在更新进行时阻塞读取线程。
也许可以考虑使用 ReentrantReadWriteLock
或其他适合此情况的东西。
英文:
So I have this fetcher code that fetches a json configuration which is a map of string to booleans (its a json schema that is guaranteed)
I have to implement this method
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;
}
I am trying to avoid multiple threads concurrently updating the map, but this I do not think completely works. If multiple threads race and calling the equals method, while one of the threads is updating, then eventually all the threads will try to contend updating it. I could of course synchronize the entirity of isFlagEnabled but that would be unnecessarily locking too and I could be serving read requests while the expensiveJsonParsingOperation is happening.
I am trying to figure out how best to
- Ensure exactly one thread updates this map when needed.
- Not block threads on reads while this update is happening.
Maybe. https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html? Or something else I can use for this case?
答案1
得分: 1
你可以使用双重检查锁定来确保只有在读者之间引入太多争用的情况下才更新一次:
public Optional<Boolean> isFlagEnabled(final String featureFlag) {
var json = retriever.retrieve();
if (!json.equals(currFlagJson)) {
synchronized (this) {
if (!json.equals(currFlagJson)) {
flags = expensiveJsonParsingOperation(json);
currFlagJson = json;
}
}
}
return Optional.ofNullable(flags.get(featureFlag));
}
请注意,如果只更新引用而不修改地图本身,则无需使用ConcurrentMap
。如果使用不可变地图,还可以从flags
中省略volatile
(依赖currFlagJson
的内存屏障来保持地图引用的新鲜)。否则,在另一个线程更新字段时,存在部分发布的风险。无论如何,currFlagJson
必须是volatile
以确保它不会被缓存。
英文:
You can use double-checked locking to make sure it only updates once without introducing too much contention between readers:
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));
}
Note that there's no need to use a ConcurrentMap
if you're only updating the reference without modifying the map itself. If you use an immutable map, you can also omit volatile
from flags
(relying on the currFlagJson
memory barrier to keep the map reference fresh). Otherwise, there's a risk of partial publication if the field is read while another thread is updating it. Either way, currFlagJson
must be volatile
to ensure it's not cached.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论