如何构建一个不重复的列表?

时间:2017-06-01 11:30:03

标签: java thread-safety

我想使用下面的代码构建一个非重复列表,但有些东西是错误的,有人说它的线程不安全但是我没有得到它,所以请给我一些例子来证明它,谢谢< / 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;  
    }  
} 

1 个答案:

答案 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方法正在同步不同的对象。这意味着putIfAbsentputIfAbsent上的直接操作方面存在竞争条件。

例如,如果您有两个主题:

  • 主题A调用list
  • 主题B调用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