本题基于Synchronizing on an Integer value .
那里的解决方案看起来很棒,只是有一个小问题,它没有解决如何从 ConcurrentHashMap
中删除值的问题。
为了解决我在下面的程序中所做的
import java.util.concurrent.ConcurrentHashMap;
public class Example {
private final ConcurrentHashMap<Integer, Integer> concurrentHashMap = new ConcurrentHashMap<Integer, Integer>();
public void doSomething(int i) {
synchronized (getLockForId(i)) {
concurrentHashMap.remove(i);
}
}
public Integer getLockForId(int id) {
concurrentHashMap.putIfAbsent(id, id); // I want to replace these two
// operation with single one
// since it seems the cause of
// NPE
return concurrentHashMap.get(id);
}
public static void main(String[] args) {
final Example example = new Example();
new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (true) {
example.doSomething(++i);
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (true) {
example.doSomething(++i);
}
}
}).start();
}
}
问题是它总是导致 NullPointerException
。我的第一个分析是因为我删除了分配给 null 的值,所以它导致了 NullPointerException
。所以我在下面做了
Object obj = new Object();
synchronized (obj) {
obj = null;
}
但以上不会导致 NullPointerException
。所以我的问题是为什么在上述情况下会抛出 NullPointerException
?
即使我这样做
public Integer getLockForId(int id) {
return concurrentHashMap.putIfAbsent(id, id);
}
它仍然导致 NullPointerException
因为它只在有其他人返回时返回值 null
最佳答案
是的,会 抛出 NullPointerException
。考虑这种模式:
Thread 1 Thread 2
putIfAbsent (puts)
get (returns non-null)
acquire monitor
putIfAbsent (doesn't put)
remove (removes value)
get (returns null)
acquire monitor (bang!)
这不是“值被分配给 null”——而是 Map.get
在没有给定键的条目时返回 null。
很难知道推荐什么,因为您的代码确实没有做任何有用的事情。如果您能说出您在真实代码中想要实现的目标,我们就有可能为您提供更好的建议。
编辑:如 Nikita 所述,仅返回 putIfAbsent
的值不起作用,因为它返回 previous 值,或 null
如果它不存在 - 而您想要条目的新值。
我怀疑您必须同步对 map 的访问,基本上,使您的 getLockId
方法相对于 remove
操作具有原子性。
关于java - 在整数上同步会导致 NullPointerException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12890214/