如何修复此竞争条件错误?
How to fix this race condition error?
我有这么简单的代码:
class B {
//....
}
public class A {
private ConcurrentSkipListMap<Long, B> map = new ConcurrentSkipListMap<>();
public void add(B b) {
long key = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) / 60;
//this area has bug
if (map.containsKey(key)) {
B oldB = map.get(key);
// work with oldB
} else {
map.put(key, b);
}
//end this area
}
}
所以,我可以从 2 个线程中获取 key。然后第一个线程转到其他路径。然后第二个线程开始。但是第一个线程还没有增值。
将您标记为 "this area has a bug" 的区域包裹在 synchronized
块中:
synchronized (map) {
if (map.containsKey(key)) {
B oldB = map.get(key);
// work with oldB
} else {
map.put(key, b);
}
}
这可以防止具有相同 key
值的两个线程同时访问映射 - 但前提是所有其他对 map
的访问也是 synchronized
并且 get
(例如,您在 class 的其他地方没有未同步的 map.get
)。
请注意,这会阻止对地图的所有并发更新,这可能会造成不可接受的瓶颈。虽然您可以使用 Long.valueOf(key)
获取可以在其上同步的实例,但没有保证可以缓存的输入范围。
相反,您也许可以将 long
映射到 Integer.valueOf
缓存的值范围(即 -128 到 127),这将为您提供更细粒度的锁定,例如
// Assuming that your clock isn't stuck in the 1960s...
Integer intKey = Integer.valueOf((int)( (longKey % 255) - 128));
synchronized (intKey) {
// ...
}
(或者,当然,您可以维护自己的密钥缓存)。
我有这么简单的代码:
class B {
//....
}
public class A {
private ConcurrentSkipListMap<Long, B> map = new ConcurrentSkipListMap<>();
public void add(B b) {
long key = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) / 60;
//this area has bug
if (map.containsKey(key)) {
B oldB = map.get(key);
// work with oldB
} else {
map.put(key, b);
}
//end this area
}
}
所以,我可以从 2 个线程中获取 key。然后第一个线程转到其他路径。然后第二个线程开始。但是第一个线程还没有增值。
将您标记为 "this area has a bug" 的区域包裹在 synchronized
块中:
synchronized (map) {
if (map.containsKey(key)) {
B oldB = map.get(key);
// work with oldB
} else {
map.put(key, b);
}
}
这可以防止具有相同 key
值的两个线程同时访问映射 - 但前提是所有其他对 map
的访问也是 synchronized
并且 get
(例如,您在 class 的其他地方没有未同步的 map.get
)。
请注意,这会阻止对地图的所有并发更新,这可能会造成不可接受的瓶颈。虽然您可以使用 Long.valueOf(key)
获取可以在其上同步的实例,但没有保证可以缓存的输入范围。
相反,您也许可以将 long
映射到 Integer.valueOf
缓存的值范围(即 -128 到 127),这将为您提供更细粒度的锁定,例如
// Assuming that your clock isn't stuck in the 1960s...
Integer intKey = Integer.valueOf((int)( (longKey % 255) - 128));
synchronized (intKey) {
// ...
}
(或者,当然,您可以维护自己的密钥缓存)。