首页 > 解决方案 > 保证在多线程环境中遍历所有值

问题描述

我有一个Set任何类型的值,一个AtomicBoolean指示该类提供的功能是否正在运行。

private Set<Object> set = new HashSet<>();
private AtomicBoolean running;

现在,我有两种方法,其中一种是将对象添加到集合中,另一种用作我的类的设置方法。

public void start() {
    // ...

    set.foreEach(someApi::addObject);

    // ...
    running.set(true);
}

public void addObject(Object o) {
    set.add(o);
    if(running.get()) {
        someApi.addObject(o);
    }
}

但是,该代码存在问题。如果在方法start迭代时从另一个线程调用该方法,则 set runningis still false. 因此,对象不会被添加到 api。

问题:我如何保证集合中的所有对象和添加的对象addObject都将准确地添加到 api 中?

我的想法:

标签: javamultithreading

解决方案


问题:如何保证集合中的所有对象以及使用 addObject 添加的对象都将准确地添加到 api 中?

您必须在这里小心,因为这接近于 ole “双重检查锁定错误”

如果我理解你的问题,你想:

  1. addObject(...)在调用. _ _start()
  2. 然后当start()被调用时,对集合中的对象调用 API 方法。
  3. 如果在调用期间添加了其他对象,则处理重叠start()
  4. 在传递给的所有对象上调用该方法一次且仅一次addObject(...)

令人困惑的是,您的 API 调用也被命名为addObject(). 我认为这与addObject(...)您的代码示例中的方法不同。我将在下面重命名它someApiMethod(...)以表明它不会递归。

不幸的是,最简单的方法是synchronized在每个方法中都有一个块:

private final Set<Object> set = new HashSet<>();
public void start() {
    synchronized (set) {
        set.forEach(someApi::someApiMethod);
    }
}
public void addObject(Object obj) {
    synchronized (set) {
            if (set.add(obj)) {
                someApi.addObject(obj);
            }
        }
    }
}

为了使其更快,将需要更复杂的代码。您可以做的一件事是使用 aConcurrentHashMap和 a AtomicBoolean running。就像是:

private final ConcurrentMap<Object, Object> map = new ConcurrentHashMap<>();
private final Set<Object> beforeStart = new HashSet<>();
private final AtomicBoolean running = new AtomicBoolean();
public void start() {
    synchronized (beforeStart) {
       for (Object obj : beforeStart) {
            doIfAbsent(obj);
       }
       running.set(true);
    }
}
public void addObject(Object obj) {
    if (running.get()) {
        doIfAbsent(obj);
    } else {
        synchronized (beforeStart) {
            // we have to test running again once we get the lock
            if (running.get()) {
                doIfAbsent(obj);
            } else {
                beforeStart.add(obj);
            }
        }
    }
}
private void doIfAbsent(Object obj) {
    if (map.putIfAbsent(obj, obj)) {
        someApi.someApiMethod(obj);
    }
}

这非常复杂,根据哈希图的大小和其他因素,它可能不会更快。


推荐阅读