HOWTO · Java
Java でコンカレント・リストを作る
この記事では、Java でコンカレントリストを作成する方法を紹介します。
並行性は、プログラムまたは関数を並行して実行するプロセスです。複数のスレッドが同じメソッドで動作する場合、時間の短縮とスループットの向上が可能になります。
Java は、List 操作の効率的な方法を可能にする CopyOnWriteArrayList クラスを提供し、関数はスレッドセーフな方法で機能します。これは、2つ以上のスレッドがリストを操作しようとすると、指定されたクラスがスレッドセーフな方法で読み取り/書き込み操作を許可することを意味します。内部的には、add または remove 関数などのリストインターフェイスのメソッドを変更すると、CopyOnWriteArrayList のコンテンツが新しい内部コピーにコピーされます。この機能により、スレッドセーフになり、並列処理が可能になります。
クラス CopyOnWriteArrayList は java.util.concurrent パッケージに含まれています。以下は、特定のクラスでの操作を示すコードブロックの例です。
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class ConcurrentListOperations {
public static void main(String[] args) {
List<Integer> temp_list = Arrays.asList(1, 2, 3);
List<Integer> list = new CopyOnWriteArrayList<>(temp_list);
new WriteThread("Writer", list).start();
new ReadThread("Reader", list).start();
}
}
class WriteThread extends Thread {
private final List<Integer> list;
public WriteThread(String name, List<Integer> list) {
this.list = list;
super.setName(name);
}
public void run() {
int count = 4;
int counter = 0;
do {
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
list.add(count++);
System.out.println(super.getName() + " done");
counter++;
} while (counter != 5);
}
}
class ReadThread extends Thread {
private final List<Integer> list;
public ReadThread(String name, List<Integer> list) {
this.list = list;
super.setName(name);
}
public void run() {
while (true) {
StringBuilder output = new StringBuilder("\n" + super.getName() + ":");
for (Integer nextVal : list) {
output.append(" ").append(nextVal);
}
System.out.println(output);
}
}
}
Java のドライバクラス
上記のプログラムでは、3つのクラスが定義されています。main メソッドを持つ最初のものはドライバークラスであり、他のものは機能するためにあります。ConcurrentListOperations クラスでは、一時リストは最初に 3つの整数で初期化されます。形成された temp_list は、ArrayList クラスの別のタイプである CopyOnWriteArrayList コンストラクターに渡されます。
クラスは、上記で定義された値で配列を初期化します。これで、copyOnWriteArrayList のインスタンスが以前に作成されたスレッドクラスに渡されます。このクラスは、リストをスレッドセーフにするだけです。したがって、リストインスタンスでの並列操作が可能になります。
Java のスレッドクラス
2つのスレッドクラスは、ReadThread と WriteThread です。クラスの実際の作業は、同じリストの読み取りと書き込みを同時に行うことです。WriteThread クラスは、スレッドを宣言する 1つの方法である Thread クラスを拡張します。受信したリストインスタンスをローカル変数に割り当て、スレッド名を初期化するパブリックコンストラクターがあります。
スレッドの実際のビジネスロジックは、run メソッドにあります。スレッドを開始するには、新しく作成されたスレッドクラスインスタンスに対して start メソッドが呼び出されます。
Java で run メソッドを使用する
WriteThread クラスの run メソッドでは、ループ条件でカウンターが初期化され、run メソッドの write クラスの反復が追跡されます。do-while ループは、反復実行の追跡カウントを維持するために使用されます。
条件付きブロック内で、Thread クラスの sleep メソッドが呼び出され、定義された時間スレッドをスリープ状態にします。この関数により、並列実行中のスレッドが一定のミリ秒の間スリープ状態になります。渡されたミリ秒が負の場合は IllegalArgumentException をスローし、スレッドが中断された場合は InterruptedException をスローします。
add メソッドは、並行スレッドによってリスト内の要素を追加するために使用されます。リストインスタンスで操作が許可されていない場合は、UnsupportedOperationException がスローされます。一方、指定された要素のクラスがリストの同じタイプでない場合は、ClassCastException をスローします。指定された値が null の場合は NullPointerException をスローし、この要素の一部のプロパティが要素の追加を妨げる場合は IllegalArgumentException をスローします。
同様に、ReadThread クラスの run メソッドでは、コンストラクターが定義されています。名前とリストを初期化します。run メソッドには、実際の read ロジックがあります。StringBuilder クラスは、出力を操作するために使用されます。append メソッドは、書き込みスレッドクラスで見つかった出力を既存の出力に追加します。
したがって、read 操作と write 操作は同時に発生し、上記の形式でコンソールに出力されます。書き込みスレッドは約 5000 ミリ秒スリープし、ライター出力は read スレッドと比較して表示される回数が少なくなります。... は、write 操作が行われていないため、スレッドが無限に実行され、同じ出力を出力することを意味します。write プロセスが成功すると、read スレッドは新しく追加された値を出力します。
出力:
Reader: 1 2 3
..
Writer done
Reader: 1 2 3 4
...
Writer done
Reader: 1 2 3 4 5