English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Javaコレクションフレームワークのスレッドセーフコード詳細

Listインターフェースの可変長配列の実装です。すべてのオプションのリスト操作を実現し、nullを含むすべての要素を許可します。Listインターフェースを実現するだけでなく、リストを格納するための内部配列のサイズを操作するための方法も提供します。(このクラスは、シンクロナイズされていないVectorクラスに似ていますが、このクラスはシンクロナイズされていません。)size、isEmpty、get、set、iterator、listIteratorの操作はすべて固定時間で実行されます。add操作は分摊された固定時間で実行されます、つまり、n個の要素を追加するにはO(n)の時間がかかります。他のすべての操作は線形時間で実行されます(大まかに言えば)。LinkedList実装の定数因子に比べて、この実装の定数因子は低いです。ArrayListの各インスタンスには容量があります。これは、リスト要素を格納するための配列のサイズを指し、常にリストのサイズ以上に少なくありません。ArrayListに要素を追加するにつれて、容量も自動的に増加します。増加戦略の詳細は指定されていませんが、これは単に要素を追加するだけで分摊された固定時間のコストが発生するだけでなく、大量の要素を追加する前に、アプリケーションはensureCapacity操作を使用してArrayListインスタンスの容量を増やすことができます。これにより、再分配の回数を減らすことができます。

この実装はシンクロナイズされていません。

もし複数のスレッドが同時に一つのArrayListインスタンスにアクセスし、その中で少なくとも一つのスレッドが構造的にリストを変更する場合、外部のシンクロナイズが必要です。(構造的な変更とは、一つまたは複数の要素を追加または削除する操作、または基底の配列のサイズを明示的に調整することを指します;要素の値を設定することは構造的な変更ではありません。)これは、リストを自然に封装するオブジェクトに対してシンクロナイズ操作を通じて完了します。そのようなオブジェクトが存在しない場合、Collections.synchronizedListメソッドを使用してリストを「包装」する必要があります。これは、リストに対する意図しない非同期アクセスを防ぐために、作成時に完了するのが最善です:

Listlist = Collections.synchronizedList(newArrayList(...));

この種のiteratorとlistIteratorメソッドが返すイテレータは迅速な失敗です:イテレータが作成された後は、リストを構造的修改するためにイテレータ自身のremoveやaddメソッドを使用しない限り、リストに対して何らかの方法で修改が行われると、イテレータはConcurrentModificationExceptionを投げます。したがって、並行修改に対処する際には、イテレータがすぐに完全に失敗し、将来の任意の不确定な時間に任意の不确定な行動が発生するリスクを冒すことなく、すぐに完全に失敗します。

注意していただきたいのは、イテレータの迅速な失敗行動は保証されません。なぜなら、通常、異期同期の変更が発生するかどうかに対して何らかの硬性の保証はできません。迅速な失敗イテレータは、ConcurrentModificationExceptionを最大限に投げることを努力します。したがって、この例外に依存してこの種のイテレータの正確性を高めるプログラムを書くのは誤りです:迅速な失敗行動はバグの検出にのみ使用されるべきです。

上記のように、現在、listコレクションを1つのスレッドが読み込み操作を行い、別のスレッドが削除操作を行うコレクションを構築しています

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
public class MyArrayList {
	/** 
   * リストを作成し、スレッドが書き込み、もう一つのスレッドが読み取ります iteratorおよびlistIteratorメソッドが返すイテレータは即座に失敗します 
   */
	public void readWrite() {
		List<Integer> nums = new ArrayList<Integer>();
		List<Integer> synNums = Collections.synchronizedList(nums);
		//書き込みスレッドを起動します 
		new WriteListThread(synNums).start();
		//削除スレッドを起動します 
		new DeleteListThread(synNums).start();
	}
	public static void main(String[] args) {
		new MyArrayList().readWrite();
	}
}
class WriteListThread extends Thread {
	private List<Integer> nums;
	public WriteListThread(List<Integer> nums) {
		super(“WriteListThread”);
		this.nums = nums;
	}
	// 要素を繰り返し書き込みます1 
	public void run() {
		while (true) {
			nums.add(new Random().nextint(1000));
			System.out.println(Thread.currentThread().getName());
		}
	}
}
class DeleteListThread extends Thread {
	private List<Integer> nums;
	public DeleteListThread(List<Integer> nums) {
		super(“DeleteListThread”);
		this.nums = nums;
	}
	// 最初の要素を削除します 
	public void run() {
		while (true) {
			try{
				System.out.println(Thread.currentThread().getName()+:”+nums.remove(0));
			}
			catch(Exception e){
				continue ;
			}
		}
	}
}

List<Integer>synNums = Collections.synchronizedList(nums);を通じて、原子操作を同期することができますが、なぜ公式のAPIサンプルでは手動で同期を追加する必要があるのでしょうか?

List list = Collections.synchronizedList(new ArrayList()); 
 synchronized(list) { 
   Iterator i = list.iterator(); // 必须是同步块内 
   while (i.hasNext()) 
     foo(i.next()); 
 } 

Collections.synchronizedListのソースコードを確認してください

SynchronizedCollection(Collection<E> c) { 
      if (c==null) 
        throw new NullPointerException(); 
    this.c = c; 
      mutex = this; 
    } 
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
public class MyArrayList {
	/** 
   * リストを作成し、スレッドが書き込み、もう一つのスレッドが読み取ります iteratorおよびlistIteratorメソッドが返すイテレータは即座に失敗します 
   */
	public void readWrite() {
		List<Integer> nums = new ArrayList<Integer>();
		List<Integer> synNums = Collections.synchronizedList(nums);
		//書き込みスレッドを起動します 
		new WriteListThread(synNums).start();
		//削除スレッドを起動します 
		new DeleteListThread(synNums).start();
	}
	public static void main(String[] args) {
		new MyArrayList().readWrite();
	}
}
class WriteListThread extends Thread {
	private List<Integer> nums;
	public WriteListThread(List<Integer> nums) {
		super("WriteListThread");
		this.nums = nums;
	}
	// 要素を繰り返し書き込みます1 
	public void run() {
		while (true) {
			nums.add(new Random().nextint(1000));
			System.out.println(Thread.currentThread().getName());
		}
	}
}
class DeleteListThread extends Thread {
	private List<Integer> nums;
	public DeleteListThread(List<Integer> nums) {
		super("DeleteListThread");
		this.nums = nums;
	}
	// 最初の要素を削除します 
	public void run() {
		while (true) {
			try{
				System.out.println(Thread.currentThread().getName()+":"+nums.remove(0));
			}
			catch(Exception e){
				continue ;
			}
		}
	}
}

コレクションの同期操作については、Collectionsの同期包装ツールクラスを使用するが、非原子操作についてはユーザーが手動で同期を行う必要があります

以下のように、スレッドを追加してコレクションを読み取ります

class ReadListThread extends Thread {
	private List<Integer> nums;
	public ReadListThread(List<Integer> nums) {}}
		super(“ReadListThread”);
		this.nums = nums;
	}
	// 要素を継続的に読み取ります、非原子操作であり、ロックを手動で追加する必要があります 
	public void run() {
		while (true) {
			//休眠し、ロックを他のスレッドに渡します 
			try {
				Thread.sleep(1000);
			}
			catch (InterruptedException e1) {
				e1.printStackTrace();
			}
			synchronized (nums) {
				if (nums.size() > 100) {
					Iterator<Integer> iter = nums.iterator();
					while (iter.hasNext()) {
						System.out.println(Thread.currentThread().getName() 
						                + :” + iter.next());
						;
					}
				} else{
					try {
						nums.wait(1000);
					}
					catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

まとめ

これで、本文におけるjavaコレクションフレームワークのスレッドシンクロ化コードの詳細についての全ての内容が終わります。皆様にお役立ていただければ幸いです。興味を持たれた方、他の関連するトピックもご覧ください。不足する点があれば、コメントをお待ちしております。皆様のサポートに感謝します。

声明:この記事の内容はインターネットから取得され、著作権者に帰属します。インターネットユーザーが自発的に貢献し、自己でアップロードしたものであり、このサイトは所有権を持ちません。また、人間による編集は行われていません。著作権侵害の可能性のある内容を見つけた場合は、メールで notice#w までお知らせください。3codebox.com(メールの際は、#を@に変更してください)で通報し、関連する証拠を提供してください。一旦確認が取れましたら、このサイトは即座に侵害する可能性のあるコンテンツを削除します。

おすすめ