[英]Should I synchronize my method?
我已經定義了一個將由多線程訪問的Synchronized ArrayList。
List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());
我想為上面的列表定義一些自定義操作,如add
, remove
, edit
, delete
。
我的操作是否需要synchronized
?
由於我已經創建了我的List synchronizedList
,這不足以確保原子操作嗎?
謝謝!
如果定義自定義操作,那些將不會從Collections.synchronizedList()
繼承同步(當前同步的方法將保持同步)。
可以這樣想:如果你有一個線程安全的類,然后你擴展,線程安全也不會擴展到新類中的新方法。 線程安全性和同步僅適用於繼承的方法,而不適用於自定義定義。
因此,是的,如果您希望同步新方法,則必須明確聲明它們是synchronized
。
被問到的問題沒有意義。 您要問的是將方法添加到對象而不是類中 - 您無法在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
)都是同步的,但您需要額外的原子性。
(更准確一點,你需要在監視器上同步時調用這兩個方法,這也應該是每個方法使用的監視器。在這種情況下,這兩個方法是同步的,因此監視器只是this
就是同步方法同步的原因。)
最后的想法
即使您不必同步覆蓋方法,如果它讀取或寫入狀態也可能是個好主意(例如上面的方案2,如果你使用volatile lastAdded
方法, volatile lastAdded
)。 所有其他mutator / accessor方法都是同步的,因此如果沒有一個方法,您的API看起來會很奇怪。 人們會問,“嘿,不應該add
同步?” 你必須說服他們不需要。 更重要的是,你實際上不會獲得任何東西,因為無論如何,當它擊中super
呼叫時,該方法最終會同步。
我沒有涵蓋上面所有可能的場景,但希望這能讓你了解事情的運作方式。
看看java.util.concurrent.CopyOnWriteArrayList 。 它可以在您不需要進行大量突變的情況下提供實時的開箱即用同步。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.