为什么即使使用同步方法,此代码也不是线程安全的?

Ull*_*las 1 java thread-safety

为什么这个代码不是线程安全的,即使我们使用synchronized方法并因此获得对Helper对象的锁定?

class ListHelper <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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Jul*_*les 11

因为列表在contains返回时被解锁,然后在add被调用时再次被锁定.其他东西可以在两者之间添加相同的元素.

如果您只想使用辅助对象中的列表,则应声明它private; 如果这样做,代码将是线程安全的,只要列表的所有操作都通过在辅助对象中同步的方法.同样值得注意的是,只要是这种情况,您就不需要使用a,Collections.synchronizedList因为您在自己的代码中提供了所有必要的同步.

或者,如果要允许列表公开,则需要同步列表中的访问权限,而不是辅助对象.以下是线程安全的:

class ListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    public boolean putIfAbsent(E x) {
        synchronized (list) {
            boolean absent = !list.contains(x);
            if (absent)
               list.add(x);
            return absent;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

不同之处在于它使用与列表中其他方法相同的锁,而不是使用不同的方法.