こんにちは、私は山を見ています。
ウェンに接続された本、複数のスレッドで最後にチャットされた使用ArrayList何が起こるか、今回は通常、共通リストについて話します:Vector、ArrayList、CopyOnWriteArrayList、SynchronizedList 。
ベクター
Vector
JDK 1.0で提供されていますが、マークは付いていませんがDeprecated
、実際には誰も使用していません。主な理由はパフォーマンスの低下であり、需要を満たしていません。
ソースコード(ソースコードはここに掲載されていません)からわかるようVector
に、配列の実装に基づいています。ほとんどすべての操作メソッドは、synchronized
キーワードを使用してメソッドの同期を実現します。この同期メソッドは、複数のスレッドなどの単一の操作をロックできます。同時にadd
、実行は同期的に実行をブロックしますが、マルチスレッドの実行add
とremove
時間はブロックしません。
ただし、キューをロックする必要があるシナリオのほとんどは、単一の操作だけでなく、キュー全体をロックすることです。つまり、Vector
予想とは異なりますが、同期操作によって発生するパフォーマンスのオーバーヘッドも増加します。したがって、シーンを使用する必要はArrayList
なく、代わりに使用できます。同期キューが必要なマルチスレッドの状況であっても、使用CopyOnWriteArrayList
してSynchronizedList
置き換えることができます。
配列リスト
ArrayList
これはJDK1.1で提供されています。Vector
後継(ArrayList
実装はVector
ほぼ同じ)であるため、すべてのArrayList
メソッドsynchronized
が削除され、同期がまったく行われず、スレッドセーフではありません。
その非スレッドセーフは、イテレータの急速な障害にも反映されています。メソッドiterator
を使用しlistIterator
てイテレータを作成した後、元のArrayList
キューが変更(追加または削除)された場合、イテレータが反復しているときにConcurrentModificationException
例外が報告されます。ソースコードから、イテレータの反復プロセス中modCount
に、キュー内の変更の数が、イテレータが作成されたときに減少した変更の数のスナップショットexpectedModCount
と等しいかどうかをチェックすることがわかります。コードは次のとおりです。
private class Itr implements Iterator<E> {
// 这段代码是从 ArrayList 中摘取的
// 只留下检查方法,略过其他代码,有兴趣的可以从源码中查看
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
3つ目のポイントは、マルチスレッドシナリオでは、要素を追加するとデータが失われたり、配列の範囲外の例外が発生したりする可能性があることです。マルチスレッドでArrayListを使用するとどうなるかについて詳しく説明しているので、ここでは詳しく説明しません。ここに。
SynchronizedList
SynchronizedList
はい、静的メソッドCollections
を使用してCollections.synchronizedList()
作成された静的内部クラスは、複合List
クラスによって実現されるカプセル化された実装です。synchronized (mutex){...}
ロックされたオブジェクトmutex
はキューオブジェクトで定義されたオブジェクトと同じであるため、ほとんどのメソッドはコードブロック同期を使用します。したがって、mutex
ロックがロックされると、キュー全体がロックされ、Vector
キュー全体をロックできないという問題が解決されます。 。したがって、複数のスレッドが同時に動作しadd
、remove
メソッドがある場合、同期実行がブロックされます。
ArrayList
以下のソースコードの注記にあるように、既存の状況でのイテレータの急速な障害は依然として存在します。イテレータを使用する場合は、手動で同期を実装する必要があります。
static class SynchronizedList<E>
extends SynchronizedCollection<E>
implements List<E> {
// 代码摘自 Collections,省略很多代码
public void add(int index, E element) {
synchronized (mutex) {
list.add(index, element);}
}
public ListIterator<E> listIterator() {
return list.listIterator(); // Must be manually synched by user
}
public ListIterator<E> listIterator(int index) {
return list.listIterator(index); // Must be manually synched by user
}
}
手動同期に注意する必要があります。グローバル同期が懸念されるため、イテレータで同期を設定するときadd
は、ロックされたオブジェクトがメソッド内のオブジェクトと同じであることを確認してください。これはフォローアップで説明されるので、ここでは拡張しません。
CopyOnWriteArrayList
CopyOnWriteArrayList
JDK 1.5以降に提供されadd
ています。まず、メソッドのソースコードを確認してください。
public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private transient volatile Object[] array;
// 代码摘自 CopyOnWriteArrayList,省略很多代码
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
public boolean addAll(Collection<? extends E> c) {
Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
if (cs.length == 0)
return false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (len == 0 && cs.getClass() == Object[].class)
setArray(cs);
else {
Object[] newElements = Arrays.copyOf(elements, len + cs.length);
System.arraycopy(cs, 0, newElements, len, cs.length);
setArray(newElements);
}
return true;
} finally {
lock.unlock();
}
}
private E get(Object[] a, int index) {
return (E) a[index];
}
/**
* {@inheritDoc}
*
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
return get(getArray(), index);
}
}
ことがわかるCopyOnWriteArrayList
助けを借りてReentrantLock
、同期の、パフォーマンスが高いsynchronized
、最適化の前に。これも配列を介して実装されますが、配列の前にキーワードが追加されます。これにより、マルチスレッドの場合に配列の可視性が実現され、より安全になります。より重要な点は、要素を追加するときの実装は、配列オブジェクトを再構築し、元の配列参照を置き換えることです。拡張方式と比較すると、スペースは削減されますが、割り当て配列のパフォーマンスオーバーヘッドも増加します。要素が取得されるとき、ロックはなく、データは直接返されます。ReentrantLock
synchronized
CopyOnWriteArrayList
volatile
CopyOnWriteArrayList
add
ArrayList
get
CopyOnWriteArrayList
のイテレータがCOWIterator
実装され、iterator
メソッドが呼び出されると、現在のキュー内の配列のスナップショットがイテレータ内の配列参照に割り当てられます。元のキューが変更された場合、キュー内の配列は他の参照を指し、イテレータ内の配列は変更されないため、マルチスレッド実行のプロセスでは、キュー内のデータは、イテレータを介して配列します。このようにしてスレッドセーフを確保する一方で、データの不整合も発生する可能性があり、その使用にのみ注意を払うことができます。
static final class COWIterator<E> implements ListIterator<E> {
/** Snapshot of the array */
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
}
CopyOnWriteArrayListとSynchronizedListを比較します
CopyOnWriteArrayList
SynchronizedList
両方と両方が同期され、実装方法で異なる戦略が採用され、それぞれの焦点が異なります。
CopyOnWriteArrayList
読み取りと書き込みの分離に焦点を当てます。データ書き込み操作(add
またはremove
)が発生すると、ロックされ、各スレッドが実行をブロックします。実行プロセスは、データコピーを作成し、オブジェクト参照を置き換えます。読み取り操作(get
またはiterator
)と同時に、読み取り操作は古いデータを読み取り、履歴データのスナップショットまたはキャッシュされたデータになる場合があります。これにより、読み取りと書き込みが同時に発生したときにデータの不整合が発生しますが、データは最終的に整合性があります。この方法は、データベースの読み取り/書き込み分離モードとほぼ同じであり、多くの機能を比較できます。
SynchronizedList
強力なデータ整合性に焦点を当てます。つまり、データ書き込み操作(add
またはremove
)が発生すると、ロックが追加され、各スレッドが実行をブロックし、操作も同じロックによってブロックされますget
。
CopyOnWriteArrayList
とSynchronizedList
2つの異なる問題からCopyOnWriteArrayList
、より少ない読み取り、より多くのシーン、SynchronizedList
読み取りと書き込みの操作効率の書き込みにおける高効率の実装は非常にバランスが取れていると結論付けることができます。より高いCopyOnWriteArrayList
。インターネットからテスト結果を借ります。
記事の最後の要約
synchronized
JDK 8より前のキーワードのパフォーマンスは比較的劣っています。JDK1.5の後に実装された同期コードを見ることができ、その多くがReentrantLock
実装されています。- マルチスレッドのシナリオでは、同期に加えて、データの可視性も考慮する必要があります
volatile
。これは、キーワードを使用して実現できます。 ArrayList
同期操作はまったくなく、スレッドセーフではありませんCopyOnWriteArrayList
そしてSynchronizedList
、スレッドセーフキューに属しますCopyOnWriteArrayList
読み取りと書き込みの分離を実現します。書き込みを減らして読み取りを増やすシナリオに適していますSynchronizedList
データは、キューのグローバルロック方法である強い整合性が必要であり、読み取り操作もロックされますVector
イテレータトラバーサルのパフォーマンスが非常に低いだけです。グローバルロックキューを考慮しない場合、純粋な読み取り操作と個々の書き込み操作のパフォーマンスにSynchronizedList
大きな違いはありません。
参照
- Java Vector(およびStack)クラスが廃止または非推奨と見なされるのはなぜですか?
- CopyOnWriteArrayListとCollections.synchronizedListのパフォーマンスの比較
- Collections.synchronizedList、CopyOnWriteArrayList、ベクターの紹介、ソースコード分析、パフォーマンスの比較
推奨読書
こんにちは、私はKanshanです。パブリックアカウント:Kanshanのコテージ、10年前のバックエンド、Apache Storm、WxJava、Cynomysのオープンソース寄稿者。主な仕事:プログラマー、アルバイト:建築家。コードの世界で泳ぎ、ドラマでの生活を楽しんでください。
個人のホームページ:https
://www.howardliu.cn個人のブログ投稿:Javaのキューを知る:Vector、ArrayList、CopyOnWriteArrayList、SynchronizedList
CSDNのホームページ:http:
//blog.csdn.net/liuxinghao CSDNのブログ投稿:キューを知るJavaの場合:Vector、ArrayList、CopyOnWriteArrayList、SynchronizedList