Javaコレクション:CopyOnWriteArrayListとSynchronizedList

8423 ワード

CopyOnWriteArrayList
まず2点を挙げます.
1.CopyOnWriteArrayListはjavaにある.util.concurrentパッケージの下で、このクラスは同時に設計されていることがわかります.
2、CopyOnWriteArrayList、その名の通り、Writeの場合は常にCopy、つまりCopyOnWriteArrayListに対して、任意の可変操作(add、set、removeなど)はコピーという動作を伴うものであり、後にCopyOnWriteArrayListの下位実装メカニズムを解読する
 
4つの注目点CopyOnWriteArrayListでの答え
注目点
結論
空の許可
はい
重複データの許可
はい
順序付け
秩序
スレッドのセキュリティ
安全
 
 
 
 
 
 
 
CopyOnWriteArrayListに要素を追加する方法
CopyOnWriteArrayListでは、追加、削除、修正、挿入の原理は同じなので、追加要素でCopyOnWriteArrayListの下位実装メカニズムを分析すればよい.
まずCopyOnWriteArrayListの構造を見てみましょう.
public class CopyOnWriteArrayList
    implements List, RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8673264195747942595L;

    /** The lock protecting all mutators */
    final transient ReentrantLock lock = new ReentrantLock();

    /** The array, accessed only via getArray/setArray. */
    private transient volatile Object[] array;
    
    .....
}

構築方法
/**
 * Creates an empty list.
 */
public CopyOnWriteArrayList() {
    setArray(new Object[0]);
}
/**
 * Sets the array.
 */
final void setArray(Object[] a) {
    array = a;
}

CopyOnWriteArrayListの場合、最下位はObject[]arrayであり、次にCopyOnWriteArrayListがインスタンス化され、Object arrayは配列サイズ0の配列を指す.
要素の追加
public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    //  
    lock.lock();
    try {
        //  
        Object[] elements = getArray();
        int len = elements.length;
        //  , +1
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        //  
        newElements[len] = e;
        //  
        setArray(newElements);
        return true;
    } finally {
        //  
        lock.unlock();
    }
}

要素を追加するたびに、CopyOnWriteArrayListは配列をコピーし、コストが非常に高いことがわかります.
読むときはロックをかける必要はなく、直接取得します.削除と追加にはロックが必要です.
2時に話さなければならないことがあります.CopyOnWriteArrayListという同時コンポーネントは、実は2つの非常に重要な分散理念を反映していると思います.
(1)読み書き分離
私たちがCopyOnWriteArrayListを読み込むときに読み取ったのはCopyOnWriteArrayListの中のObject[]arrayですが、修正するときに操作するのは新しいObject[]arrayで、読み書き操作は同じオブジェクトではなく、これが読み書き分離です.このような技術データベースは非常に多く使われており、高と下でデータベースの圧力を緩和するために、キャッシュをしてもデータベースを読み書き分離し、読むときはライブラリを使用し、書くときはライブラリを使用し、それからライブラリを読み、ライブラリを書く間で一定の同期を行うことで、同じライブラリで読み書きするIO操作が多すぎることを避けることができる.
(2)最終一致
CopyOnWriteArrayListでは,スレッド1が集合中のデータを読み取ることは,必ずしも最新のデータではない.スレッド2、スレッド3、スレッド4の4つのスレッドはいずれもCopyOnWriteArrayListの中のデータを修正しているが、スレッド1が入手したのはやはり最も古いObject[]arrayであり、新たに追加されたデータはないので、スレッド1が読み取った内容は必ずしも正確ではない.しかし、これらのデータはスレッド1に対しては一致しないが、その後のスレッドに対しては一致しているに違いない.それらが得たObject[]arrayは、3つのスレッドが動作した後のObject array[]に違いない.これが最終的な一致である.最終的な一貫性は分散システムにとっても重要であり、一定時間のデータの不一致を許容することによって、分散システム全体の可用性とパーティションフォールトトレランスを向上させる.もちろん、最終的な一致はいかなるシーンにも適用されるわけではありません.駅の切符販売のようなシステムのユーザーはデータのリアルタイム性に対する要求が非常に高く、強い一致性をしなければなりません.
最後に、CopyOnWriteArrayListの要素が増加するにつれて、CopyOnWriteArrayListの修正コストはますます高くなるので、CopyOnWriteArrayListは、修正操作よりも読み取り操作がはるかに多い同時シーンに適用される.
 
Collections.SynchronizedList
これはCollectionsの静的内部クラスであり、Collectionsの別の内部クラスであるSynchronizedCollectionsに継承されます.Collectionsを使用することができます.synchronizedList(List list)メソッドは、List可能なインスタンスを入力する必要があるため、非安全なListインスタンスを安全なListに変換することができる.
まず、SynchronizedCollectionの構造を見てみましょう.
static class SynchronizedCollection implements Collection, Serializable {
        private static final long serialVersionUID = 3053995032091335093L;

    final Collection c;  // Backing Collection
    final Object mutex;     //  

    SynchronizedCollection(Collection c) {
        this.c = Objects.requireNonNull(c);
        //  this
        mutex = this;
    }
    
    .....
}

その追加と削除方法に同期コードブロックを追加する
public boolean add(E e) {
    synchronized (mutex) {
       return c.add(e);
    }
}
public boolean remove(Object o) {
    synchronized (mutex) {
        return c.remove(o);
    }
}

したがって、Collectionsクラスでは、同期コードブロックが単純に利用されており、元のスレッドが安全ではない集合であり、その内部クラスSynchronizedCollectionsを継承し、それぞれ独自の方法を書き換えてスレッドの安全を達成している.