我有一个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
方法正在迭代集合 running
仍然是false
。因此,该对象不会添加到 api。
问题:我如何保证集合中的所有对象以及添加 addObject
的对象是否会被添加到 api 一次?
我的想法:
- 使用锁并阻止
addObject
method 如果安装程序当前正在向 api 添加方法(或者使这两个方法都为synchronized
,这会稍微降低性能)
最佳答案
Question: How can i guarantee that all objects in the set and objects added with addObject will be added to the api exactly one time?
这里你必须小心,因为这接近 ole "double check locking bug" .
如果我理解你的问题,你想:
- 在调用
start()
之前,将传入addObject(...)
的对象放入集合中。 - 然后,当调用
start()
时,对集合中的对象调用 API 方法。 - 如果在调用
start()
期间添加了其他对象,则处理重叠 - 对传递给
addObject(...)
的所有对象调用该方法一次且仅一次。
令人困惑的是您的 API 调用也被命名为 addObject()
。我认为这与代码示例中的 addObject(...)
方法不同。我将在下面将其重命名为 someApiMethod(...)
以表明它不会递归。
不幸的是,最简单的方法是在每个方法中都有一个 synchronized
block :
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);
}
}
}
}
为了使其更快,需要使用更复杂的代码。您可以做的一件事是使用 ConcurrentHashMap 和 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);
}
}
这非常复杂,并且可能不会更快,具体取决于您的 HashMap 有多大和其他因素。
关于java - 保证在多线程环境中迭代所有值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51370387/