java - 如何修复这个竞争条件错误?

标签 java multithreading java-8 race-condition

我有这样简单的代码:

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。然后第一个线程转到 else-path。然后第二个线程开始。但第一个线程还没有增加值(value)。

最佳答案

将您标记为“此区域有错误”的区域包装在 synchronized block 中:

synchronized (map) {
    if (map.containsKey(key)) {
        B oldB = map.get(key);
        // work with oldB
    } else {
        map.put(key, b);
    }
}

这可以防止具有相同key值的两个线程同时访问 map - 但前提是对map的所有其他访问也同步get (例如,您在类中的其他地方没有不同步的 map.get)。

请注意,这会阻止对 map 的所有并发更新,这可能会造成 Not Acceptable 瓶颈。虽然您可以使用 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) {
  // ...
}

(或者,当然,您可以维护自己的 key 缓存)。

关于java - 如何修复这个竞争条件错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38706840/

相关文章:

java - 将包含二进制的整数列表转换为十六进制

Android - 当 Activity 完成时线程会发生什么?

java - 多线程环境中的循环列表

java - 多个线程写入单个数据库表

java - 使用 java 流解析列表

java - 将 Future 与 ExecutorService 结合使用

java - Java 8 Streams 中的可变参数

JavaFX 导出到 jar。未找到主类

java - 获取Set中的多个连接数据

java - retrofit 时禁用 SPDY 连接