同時に複数の処理を制御したい。
スレッドとは、ソースを連続的に実行する流れです。
Java プログラムが実行されると、1つのスレッドが生成されます。さらに、そのスレッドから別のスレッドを起動することが出来ます。このようにして、一つのプログラム中に複数のスレッドを持ったものを、マルチスレッドと呼びます。
通常、マルチスレッドを行うためには、メモリ領域や CPU 時間などのコンピュータ上の資源の割り当てなどの処理を実装しなければならないなど多くの手間がかかりますが、Java ではそれらの処理をJavaVM が行うため、基本的な処理を実装するのみでマルチスレッドを実現することが出来ます。
スレッドを作成するには Thread オブジェクトとして作成して実行しますが、これには2つの作成方法があります。
上記で説明した作成方法で実際にスレッドの作成を行います。
class Test extends Thread { //スレッドクラスを継承する public void start() { System.out.println("-- Start --"); super.start(); } public void run() { //スレッドで実行する処理を記述 System.out.println("-- run --");
for (int i=0; i <= 5; i++) {
System.out.println("Count: " + i); }
System.out.println("-- run end --");
}
} class TestMaint { //メイン処理
//Threadクラスを継承したオブジェクトを生成・実行
public static void main(String[] args) { Test thread = new Test(); System.out.println("-- Thread New --"); thread.start(); } }
上記の処理結果は以下のようになります。
-- Thread New --
-- Start --
-- run --
Count: 0
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
-- run end --
import java.io.*;
class TestA implements Runnable { public void run() { for (int i=0; i <= 5; i++) {
System.out.println("TestA: " + i); }
} } class TestB implements Runnable { public void run() {
for (int i=5; i >= 0; i--) { System.out.println(" TestB: " + i); } } } class TestMain {
public static void main(String[] args) {
// ランナブルクラスのインスタンス化
TestA testRunA = new TestA();
TestB testRunB = new TestB();
System.out.println("ランナブルクラスのインスタンス化終了");
// スレッドのインスタンス化
Thread TestA = new Thread(testRunA);
Thread TestB = new Thread(testRunB);
System.out.println("スレッドへの受け渡し終了"); // スレッドの開始 TestA.start(); TestB.start();
System.out.println("スレッドをスタート");
} }
上記の処理結果は以下のようになります。
ランナブルクラスのインスタンス化終了
スレッドへの受け渡し終了
スレッドをスタート
TestA: 0
TestB: 5
TestA: 1
TestB: 4
TestA: 2
TestB: 3
TestA: 3
TestB: 2
TestA: 4
TestB: 1
TestA: 5
TestB: 0
Thread クラス、Runnable インターフェースを利用し新たな、スレッドを作成し並列処理を行なうことが出来ます。しかし、並列処理の中で、同じファイルへのアクセスなどのように、同時に処理を行なうことが出来ない処理もあります。その場合、synchronized キーワードを使って排他処理を行なうことが出来ます。
次のように書くことで実現します。メソッドに排他処理をつけることによって、このメソッドは同時に多数から呼び出されることはありません。必ず排他がかかります。
public synchronized void test(){ List list = new Stack(); list.add("ABC"); list.add("BCD"); list.add("CDE"); Iterator iter = list.iterator(); while(iter.hasNext()){ System.out.println(iter.next()); } }
上記で、synchronized は、クラス・メソッドに対して他処理からの呼び出しを防ぐものでしたが、volatile は変数に対して指定します。volatile
に指定された変数はメモリーからのロードと格納のタイミングについて制限がかけられます。
以下が volatile を利用した簡単なプログラムの例です。
public class TestA { volatile int a; public TestA(int a) { this.a = a; } public void testA() { for (int i = 0; i < 5; i++) { a = a + 2; } System.out.println("計算結果は「" + a + "」になります"); } }
上記のように記述することによって変数「int a」は、複数の thread の処理によってその値に矛盾が生じるのを防ぐことが出来ます。