我已定义了一个可由多线程访问的Synchronized ArrayList。
List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());
我想为上面的列表定义一些自定义操作,例如add
,remove
,edit
,delete
。
我的操作是否需要synchronized
?
由于我已经制作了我的列表synchronizedList
,这不足以确保原子操作吗?
谢谢!
答案 0 :(得分:2)
如果您定义自定义操作,那些不将从Collections.synchronizedList()
继承同步(当前同步的方法将保持同步)。
可以这样想:如果你有一个线程安全类,然后你可以扩展,那么线程安全性也不会扩展到新类中的新方法。线程安全和同步仅适用于继承的方法,而不适用于自定义定义。
因此,是的,如果您希望同步新方法,则必须明确声明它们synchronized
。
答案 1 :(得分:1)
提出的问题没有意义。您要问的是将方法添加到对象而不是类中 - 您无法在Java中执行此操作。 Collections.synchronizedList
选择了List的类 - 它是java.util.Collections.SynchronizedRandomAccessList
或...SynchronizedList
。此外,每个都是包私有的,所以你不能扩展它们。
但是,假设您确实有类似于提供的实现:
public class SyncedList<E> implements List<E> {
private final List<E> delegate;
public SyncedList(List<E> delegate) { this.delegate = delegate; }
@Override
public synchronized boolean add(E e) {
return delegate.add(e);
}
@Override
public synchronized void clear() {
delegate.clear();
}
// ... etc
现在你扩展这个类:
public class MySyncedList<E> extends SyncedList<E> {
...
现在你的问题是:我们如何处理覆盖方法?答案是......这取决于它!
要记住的一般规则是覆盖方法不继承其超级synchronized
修饰符。但你可能不需要它。以下是一些场景:
场景1:根本不调用super
方法
示例:
@Override
public void clear() {
throw new UnsupportedOperationException();
}
在这种情况下,应该清楚(双关语警!)您不需要任何同步,因为没有什么可以同步。 (琐碎的例子,但很容易让路。)
场景2:在不触及其他可变状态的情况下调用一个super
方法
示例:
private final E dontAllow; // assume this is set in the constructor
@Override
public boolean add(E e) {
if (Objects.equals(e, dontAllow))
throw new IllegalArgumentException("can't add element: " + e);
super.add(e);
}
在这种情况下,您实际上不需要同步add
。对super.add(e)
的调用将保持其同步,并且您没有触及任何其他可变状态,因此没有任何额外的同步。
场景3:调用一个super
方法,但也触及可变状态
示例:
private E lastAdded = null;
@Override
public synchronized boolean add(E e) {
boolean result = super.add(e); // do this first in case it throws an exception
lastAdded = e;
return result;
}
public synchronized E getLastAdded() {
return lastAdded;
}
这里我们添加了其他状态,因此我们必须同步它!请注意,在这种特殊情况下,您可能也使lastAdded
变为volatile并且没有使这两种方法同步 - 但在其他情况下,这可能是不可能的。
场景4:在super
上多调用一个方法
示例:
public synchronized void add(E e) {
if (indexOf(e) >= 0)
return false;
return super.add(e);
}
这里你需要同步,假设你关心原子性。它不是从super.add
继承而来的。方法(indexOf
和super.add
)中的每个调用都是同步的,但您需要额外的原子性。
(更精确一点,你需要在监视器上同步时调用这两个方法,这也应该是每个方法使用的监视器。在这种情况下,这两个方法是同步的,所以monitor只是this
,这是同步方法同步的。)
最后的想法
即使您不必同步覆盖方法,如果它读取或写入状态也可能是个好主意(例如,上面的方案2,如果使用volatile lastAdded
方法,则为3)。所有其他mutator / accessor方法都是同步的,因此如果没有一个方法,您的API看起来会很奇怪。人们会问,“嘿,不应该add
同步?”你必须说服他们不需要。更重要的是,你实际上不会获得任何东西,因为当它到达super
电话时,该方法最终会同步。
我没有涵盖上述所有可能的情况,但希望能让您了解事情的运作方式。
答案 2 :(得分:1)
看看java.util.concurrent.CopyOnWriteArrayList。在您不需要进行大量突变的情况下,它提供了开箱即用的实时同步。