我想使用下面的代码构建一个非重复列表,但有些东西是错误的,有人说它的线程不安全但是我没有得到它,所以请给我一些例子来证明它,谢谢< / p>
class BadListHelper <E> {
public List<E> list = Collections.synchronizedList(new ArrayList<E>());
public synchronized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if (absent)
list.add(x);
return absent;
}
}
答案 0 :(得分:2)
代码的一个问题是对(公开的)public interface AListener extends ParseTreeListener {
void enterReturn(AParser.ReturnContext ctx);
void exitReturn(AParser.ReturnContext ctx);
void enterBreak(AParser.BreakContext ctx);
void exitBreak(AParser.BreakContext ctx);
void enterMult(AParser.MultContext ctx);
void exitMult(AParser.MultContext ctx);
void enterAdd(AParser.AddContext ctx);
void exitAdd(AParser.AddContext ctx);
void enterInt(AParser.IntContext ctx);
void exitInt(AParser.IntContext ctx);
}
对象的操作以及list
方法正在同步不同的对象。这意味着putIfAbsent
在putIfAbsent
上的直接操作方面存在竞争条件。
例如,如果您有两个主题:
list
helper.list.add(e)
那么你可能会在列表中以helper.putIfAbsent(e)
两次结束......具体取决于时间。
e
现在可以肯定的是,如果线程A和B都直接调用public synchronized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
// <<--- the Thread A call could happen here.
if (absent) {
list.add(x);
}
return absent;
}
,则会得到相同的效果。但是,helper.list.add
的隐含语义是它不会添加已存在的元素......这就是它在上面的例子中所做的。
实际上,putIfAbsent
的实现会返回一个自身同步的Collections.synchronizedList
对象。因此,一个解决方法是将List
更改为:
putIfAbsent