如何修复此竞争条件错误?

时间:2016-08-01 19:39:30

标签: 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个线程获得密钥。然后第一个线程转到else-path。然后第二个线程开始。但是第一个线程还没有增值。

1 个答案:

答案 0 :(得分:3)

将您标记为&#34的区域包裹起来;此区域有错误&#34;在synchronized块中:

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

这可以防止具有相同key值的两个线程同时访问地图 - 但仅当map的所有其他访问也synchronizedget时(例如,你在课堂的其他地方没有不同步的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) {
  // ...
}

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