我在多线程环境中有一个有状态bean,它将状态保存在地图中。现在我需要一种方法来在一个原子动作中替换该映射的所有值。
public final class StatefulBean {
private final Map<String, String> state = new ConcurrentSkipListMap<>();
public StatefulBean() {
//Initial state
this.state.put("a", "a1");
this.state.put("b", "b1");
this.state.put("c", "c1");
}
public void updateState() {
//Fake computation of new state
final Map<String, String> newState = new HashMap<>();
newState.put("b", "b1");
newState.put("c", "c2");
newState.put("d", "d1");
atomicallyUpdateState(newState);
/*Expected result
* a: removed
* b: unchanged
* C: replaced
* d: added*/
}
private void atomicallyUpdateState(final Map<String, String> newState) {
//???
}
}
目前我使用ConcurrentSkipListMap
作为ConcurrentMap
的实现,但这不是必需的。
我认为解决此问题的唯一方法是制作全局state
volatile
并完全替换地图或使用AtomicReferenceFieldUpdater
。
还有更好的方法吗?
我的更新非常频繁,每秒一次或两次,但只有很少的值。此外,整个地图只包含少于20个值。
答案 0 :(得分:1)
最简单,最简单的方法是切换地图而不是替换地图内容。无论是使用volatile
还是AtomicReference
(我不明白为什么你特别需要AtomicReferenceFieldUpdater
),不应该产生太多差异。
这可确保您的地图始终处于正确状态,并允许您提供快照。但它并不能保护您免受其他并发问题的影响,因此如果丢失更新等问题是您需要的更多代码(尽管AtomicReference
将为您提供CAS
方法来处理这些问题)。
如果您只考虑地图的完整原子替换,问题实际上相当简单。知道其他操作对地图的影响以及如何影响将是有益的。我还想了解为什么ConcurrentSkipListMap
被选为ConcurrentHashMap
。
答案 1 :(得分:0)
扩展您选择的地图实现并添加同步方法:
class MyReplaceMap<K, V> extends HashMap<K, V> //or whatever
{
public synchronized void replaceKeys(final Map<K, V> newMap)
{
//.. do some stuff
}
}
当然,你总是可以使state
非最终的易失性并重新分配(assignment is atomic)
private volatile Map<String, String> state = new HashMap<>();
//...
final Map<String, String> newState = new HashMap<>();
newState.put("b", "b1");
newState.put("c", "c2");
newState.put("d", "d1");
state = newState;
答案 2 :(得分:0)
由于地图非常小,所以只需在您访问它的所有地方使用synchronized
即可。
private void atomicallyUpdateState(final Map<String, String> newState) {
synchronized(state) {
state.clear();
state.putAll(newState);
}
}
但不要忘记任何事情,比如像
这样的事情String myStatevalue = state.get("myValue");
需要成为
String myStatevalue;
synchronized (state) {
myStatevalue = state.get("myValue");
}
否则读取和更新不同步并导致竞争条件。
答案 3 :(得分:0)
由于客户端代码维护对bean的引用而不是地图,因此替换值(即整个地图)似乎是最简单的解决方案。
除非出现任何重大的性能问题(尽管除非地图很大,否则使用锁定可能会表现得更差,而且可预测性更差)我会在需要更高级知识的任何事情之前尝试锁定。
功能程序员如何做到这一点。
答案 4 :(得分:0)
使用CAS和AtomicReference
的方法是在每次批量更新时复制地图内容。
AtomicReference<Map<String, String>> workingMapRef = new AtomicReference<>(new HashMap<>());
此地图可以是并发的,但对于&#34;批量更新&#34;它是只读的。然后在updateState
循环doUpdateState()
,直到您变为真,这意味着您的值已更新。
void updateState() {
while (!doUpdateState());
}
boolean doUpdateState() {
Map<String, String> workingMap = workingMapRef.get();
//copy map content
Map<String, String> newState = new HashMap<>(workingMap); //you can make it concurrent
newState.put("b", "b1");
newState.put("c", "c2");
newState.put("d", "d1");
return workingMapRef.compareAndSet(workingMap, newState);
}
答案 5 :(得分:0)
使用ReadWriteLock
可以自动替换Map中的所有值。
private static final ReadWriteLock LOCK = new ReentrantReadWriteLock();
private void atomicallyUpdateState(final Map<String, String> newState) {
LOCK.writeLock().lock();
try {
state.clear();
state.putAll(newState);
} finally {
LOCK.writeLock().unlock();
}
}