簡體   English   中英

我應該同步我的方法嗎?

[英]Should I synchronize my method?

我已經定義了一個將由多線程訪問的Synchronized ArrayList。

List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());

我想為上面的列表定義一些自定義操作,如addremoveeditdelete

我的操作是否需要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繼承而來的。 方法中的每個調用( indexOfsuper.add )都是同步的,但您需要額外的原子性。

(更准確一點,你需要在監視器上同步時調用這兩個方法,這也應該是每個方法使用的監視器。在這種情況下,這兩個方法是同步的,因此監視器只是this就是同步方法同步的原因。)

最后的想法

即使您不必同步覆蓋方法,如果它讀取或寫入狀態也可能是個好主意(例如上面的方案2,如果你使用volatile lastAdded方法, volatile lastAdded )。 所有其他mutator / accessor方法都是同步的,因此如果沒有一個方法,您的API看起來會很奇怪。 人們會問,“嘿,不應該add同步?” 你必須說服他們不需要。 更重要的是,你實際上不會獲得任何東西,因為無論如何,當它擊中super呼叫時,該方法最終會同步。

我沒有涵蓋上面所有可能的場景,但希望這能讓你了解事情的運作方式。

看看java.util.concurrent.CopyOnWriteArrayList 它可以在您不需要進行大量突變的情況下提供實時的開箱即用同步。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM