首页 > 解决方案 > 使用 UUID 作为键时映射的线程安全

问题描述

有一个包含final字段的服务。

@Service
public class RegularService {
    private final DataMap map = new DataMap();
    ....
}

final字段的类DataMap看起来像这样

class DataMap {
    private Map<UUID, String> content1 = new HashMap<>();
    private Map<UUID, String> content2 = new HashMap<>();

    void updateContent(UUID id, String data) {
        if (content1.containsKey(id)) {
           ....
           return;
        }


        if (content2.containsKey(id)) {
            content1.put(id, data);
            content2.remove(id);
            return;
        }
        content2.put(id, data);
    }

问题是 - 是否updateContent具有竞争条件?问题是,UUID理论上使用意味着不同的线程永远不会访问相同的条目......

如果是,那么是否需要同步整个updateContent方法或仅使用ConcurrentHashMap就足够了?

标签: javaspringconcurrency

解决方案


使用 UUID 作为键时映射的线程安全

映射的线程安全实际上与所讨论的关键对象无关。仅仅因为UUID.randomUUID()返回一个唯一的 UUID 并不能以某种方式保证线程安全。问题在于内存同步以及线程如何查看和发布对映射的更改以及协调多个映射操作。

问题是 - 是否updateContent()具有竞争条件?

是的,它确实。首先,您不能在Map不使用同步或并发Map实现的情况下更新使用多个线程。 ConcurrentHashMap将负责对映射的修改和线程之间内存的发布,以使它们保持同步。但是因为您要对需要协调的地图进行多项操作,所以您需要添加更多同步。

如果是,那么是否需要同步整个updateContent()方法或仅使用 ConcurrentHashMap 就足够了?

由于您要对 2 个地图进行多项更改,因此您需要使用synchronized或以其他方式锁定多个操作。一旦你这样做了,就没有必要使用ConcurrentHashMap. 您可以制作方法synchronized,锁定其中一张地图,或制作特定final Object lockObject = new Object()用于锁定操作。

例如,如果没有锁,则没有任何东西可以保护 thread1 看到content1不包含 id XXX 的内容,然后content2在 thread2 将其添加到之前继续检查content1。所以thread1会覆盖thread2 data,这不是你想要的。


推荐阅读