第 13 回: スレッドを作る方法, まとめ, 二つのスレッドが同じフィールドに代入する例, 正しい例, スレッドを止める, 待たせる, 終了を待つ, スレッド同士を待ち合わせる.

スレッドを作る方法

Thread クラスの拡張クラスを作る:

1. Thread クラスの拡張クラスを作る
2. そのクラスの run メソッドを宣言する
3. そのクラスのインスタンスを作る
4. start メソッドを呼び出す

CountTenA.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class CountTenA extends Thread{
    public static void main(String[] args){
	CountTenA ct = new CountTenA();
	ct.start();
	for(int i = 0; i < 10; i++){
	    System.out.println("main:i = " + i);
	}
    }

    public void run(){
	for(int i = 0; i < 10; i++){
	    System.out.println("run:i = " + i);
	}
    }
}

CountTenA.java の実行結果は:

[wtopia koji]$ java CountTenA
main:i = 0
main:i = 1
main:i = 2
main:i = 3
main:i = 4
main:i = 5
main:i = 6
main:i = 7
main:i = 8
main:i = 9
run:i = 0
run:i = 1
run:i = 2
run:i = 3
run:i = 4
run:i = 5
run:i = 6
run:i = 7
run:i = 8
run:i = 9

Runnable インターフェースを実装したクラスを作る:

1. Runnable インターフェースを実装したクラスを作る.
2. run メソッドを実装する.
3. そのクラスのインスタンスを作り, Thread オブジェクトを生成する.
4. Thread オブジェクトの start メソッドを呼び出す.

CountTenB.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class CountTenB implements Runnable{
    public static void main(String[] args){
	CountTenB ct = new CountTenB();
	Thread th = new Thread(ct);
	th.start();
	for(int i = 0; i < 10; i++){
	    System.out.println("main:i = " + i);
	}
    }

    public void run(){
	for(int i = 0; i < 10; i++){
	    System.out.println("run:i = " + i);
	}
    }
}

CountTenB.java の実行結果は:

[wtopia koji]$ java CountTenB
main:i = 0
main:i = 1
main:i = 2
main:i = 3
main:i = 4
main:i = 5
main:i = 6
main:i = 7
main:i = 8
main:i = 9
run:i = 0
run:i = 1
run:i = 2
run:i = 3
run:i = 4
run:i = 5
run:i = 6
run:i = 7
run:i = 8
run:i = 9

Thread を作る方法のまとめ

                      方法 A                    方法 B
用途:                   簡単なクラス向け        複雑なクラス向け
スーパークラス:      Thread クラス              何でもよい
クラス宣言:                Thread クラスの拡張   Runnable インターフェースの実装
スレッドの起動:      start メソッド              Thread 経由で start を起動
実行開始点:                run メソッド                run メソッド

二つのスレッドが同じフィールドに代入する例

矛盾が発生している.

BadBank.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class BadBank{
    private int value = 0;
    public void addMoney(int money){
	int currentValue = value;
	System.out.println(Thread.currentThread() + "が addMoney に入った.");
	value += money;
	if(currentValue + money != value){
	    System.out.println(Thread.currentThread() + "で矛盾が発生した.");
	    System.exit(-1);
	}
	System.out.println(Thread.currentThread() + "が addMoney から出た.");
    }
}

BadBankTest.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class BadBankTest extends Thread{
    BadBank bank;
    public BadBankTest(BadBank bank){
	this.bank = bank;
    }
    public void run(){
	while(true){
	    bank.addMoney(100);
	    bank.addMoney(-100);
	}
    }
    public static void main(String[] args){
	BadBank bank = new BadBank(); // BadBank を生成
	new BadBankTest(bank).start();
	new BadBankTest(bank).start();
    }
}

BadBank.java と BadBankTest.java の実行結果は:

[wtopia koji]$ javac BadBank.java BadBankTest.java
[wtopia koji]$ java BadBankTest
Thread[Thread-0,5,main]が addMoney に入った.
Thread[Thread-0,5,main]が addMoney から出た.
Thread[Thread-0,5,main]が addMoney に入った.
Thread[Thread-0,5,main]が addMoney から出た.
Thread[Thread-0,5,main]が addMoney に入った.
Thread[Thread-0,5,main]が addMoney から出た.
Thread[Thread-0,5,main]が addMoney に入った.
Thread[Thread-0,5,main]が addMoney から出た.

... (省略)

Thread[Thread-1,5,main]が addMoney から出た.
Thread[Thread-0,5,main]が addMoney に入った.
Thread[Thread-0,5,main]で矛盾が発生した.
Thread[Thread-1,5,main]が addMoney に入った.
Thread[Thread-1,5,main]で矛盾が発生した.

二つのスレッドが同じフィールドに代入する正しい例

GoodBank.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class GoodBank{
    private int value = 0;
    public synchronized void addMoney(int money){
	int currentValue = value;
	System.out.println(Thread.currentThread() + "が addMoney に入った.");
	value += money;
	if(currentValue + money != value){
	    System.out.println(Thread.currentThread() + "で矛盾が発生した.");
	    System.exit(-1);
	}
	System.out.println(Thread.currentThread() + "が addMoney から出た.");
    }
}

GoodBankTest.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class GoodBankTest extends Thread{
    GoodBank bank;
    public GoodBankTest(GoodBank bank){
	this.bank = bank;
    }
    public void run(){
	while(true){
	    bank.addMoney(100);
	    bank.addMoney(-100);
	}
    }
    public static void main(String[] args){
	GoodBank bank = new GoodBank(); // GoodBank を生成
	new GoodBankTest(bank).start();
	new GoodBankTest(bank).start();
    }
}

GoodBank.java と GoodBankTest.java の実行結果は:

[wtopia koji]$ javac GoodBank.java GoodBankTest.java
[wtopia koji]$ java GoodBankTest
Thread[Thread-0,5,main]が addMoney に入った.
Thread[Thread-0,5,main]が addMoney から出た.
Thread[Thread-0,5,main]が addMoney に入った.
Thread[Thread-0,5,main]が addMoney から出た.
Thread[Thread-0,5,main]が addMoney に入った.
Thread[Thread-0,5,main]が addMoney から出た.
Thread[Thread-0,5,main]が addMoney に入った.
Thread[Thread-0,5,main]が addMoney から出た.

... (省略)

... (永遠に繰り返していいく)

スレッドを止める

class Runner extends Thread{
  private boolean running = true;
  public void stopRunning(){
    running = faluse;
  }
  public void run(){
    while(running){
      doSomething();
    }
  }
}

スレッドを待たせる

Periodic.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class Periodic{
    public static void main(String[] args){
	for(int i = 0; i < 10; i++){
	    int tm = i * 1000;
	    System.out.println("Start sleep:tm = " + tm);
	    try{
		Thread.sleep(tm);
	    }catch(InterruptedException e){
		//
	    }
	}
    }
}

Periodic.java の実行結果は:

[wtopia koji]$ java Periodic
Start sleep:tm = 0
Start sleep:tm = 1000
Start sleep:tm = 2000
Start sleep:tm = 3000
Start sleep:tm = 4000
Start sleep:tm = 5000
Start sleep:tm = 6000
Start sleep:tm = 7000
Start sleep:tm = 8000
Start sleep:tm = 9000

スレッドを終了を待つ

JoinTest.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class JoinTest extends Thread{
    public static void main(String[] args){
	JoinTest th = new JoinTest();
	System.out.println("main:はじめ");
	System.out.println("main:終了待ちに入る");
	th.start();
	try{
	    th.join();
	}catch(InterruptedException e){
	    System.out.println(e);
	}
	System.out.println("main:終わり");
    }

    public void run(){
	System.out.println("run:スレッド実行開始");
	try{
	    Thread.sleep(5000);
	}catch(InterruptedException e){
	    System.out.println(e);
	}
	System.out.println("run:スレッド実行終了");
    }
}

JoinTest.java の実行結果は:

[wtopia koji]$ java JoinTest
main:はじめ
main:終了待ちに入る
run:スレッド実行開始
run:スレッド実行終了
main:終わり

スレッド同士を待ち合わせる

ProducerConsumer.java

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
public class ProducerConsumer{
    public static void main(String[] args){
	MyQueue queue = new MyQueue(3);
	Producer producer = new Producer(queue);
	Consumer consumer = new Consumer(queue);
	producer.start();
	consumer.start();
    }
}

class MyQueue{
    int[] intbuf;
    int start;
    int count;
    public MyQueue(int size){
	intbuf = new int[size];
	start = 0;
	count = 0;
    }
    public synchronized void put(int n) throws InterruptedException{
	while(count >= intbuf.length){
	    System.out.println(Thread.currentThread().getName() + " wait: バッファの空きを待つ");
	    wait();
	}
	int end = (start + count) % intbuf.length;
	intbuf[end] = n;
	count++;
	notifyAll();
    }
    public synchronized int take() throws InterruptedException{
	while(count == 0){
	    System.out.println(Thread.currentThread().getName() + " wait: データを待つ");
	    wait();
	}
	int n = intbuf[start];
	start = (start + 1) % intbuf.length;
	count--;
	notifyAll();
	return n;
    }
}

class Producer extends Thread{
    static final int END = -1;
    MyQueue queue = null;
    Producer(MyQueue queue){
	this.queue = queue;
    }
    public void run(){
	try{
	    for(int i = 0; i < 100; i++){
		int n = produce(i);
		queue.put(n);
	    }
	    queue.put(Producer.END);
	}catch(InterruptedException e){
	    
	}
    }
    int produce(int n){
	sleepRandomly();
	System.out.println("Producer: " + getName() + "は " + n + " を生産完了");
	return n;
    }
    void sleepRandomly(){
	try{
	    int n = (int)(Math.random() * 1000);
	    Thread.sleep(n);
	}catch(InterruptedException e){
	}
    }
    
}

class Consumer extends Thread{
    MyQueue queue = null;
    Consumer(MyQueue queue){
	this.queue = queue;
    }
    public void run(){
	try{
	    while(true){
		int n = queue.take();
		if(n == Producer.END){
		    break;
		}
		consume(n);
	    }
	}catch(InterruptedException e){
	    //
	}
    }
    void consume(int n){
	System.out.println("Consumer: " + getName() + "は " + n + " を消費中");
	sleepRandomly();
    }
    void sleepRandomly(){
	try{
	    int n = (int)(Math.random() * 1000);
	    Thread.sleep(n);
	}catch(InterruptedException e){
	    //
	}
    }
}

ProducerConsumer.java の実行結果は:

[wtopia koji]$ java ProducerConsumer
Thread-1 wait: データを待つ
Producer: Thread-0は 0 を生産完了
Consumer: Thread-1は 0 を消費中
Producer: Thread-0は 1 を生産完了
Producer: Thread-0は 2 を生産完了
Producer: Thread-0は 3 を生産完了
Consumer: Thread-1は 1 を消費中
Consumer: Thread-1は 2 を消費中
Consumer: Thread-1は 3 を消費中
Producer: Thread-0は 4 を生産完了
Consumer: Thread-1は 4 を消費中
Thread-1 wait: データを待つ
Producer: Thread-0は 5 を生産完了
Consumer: Thread-1は 5 を消費中
Thread-1 wait: データを待つ
Producer: Thread-0は 6 を生産完了
Consumer: Thread-1は 6 を消費中
Thread-1 wait: データを待つ
Producer: Thread-0は 7 を生産完了
Consumer: Thread-1は 7 を消費中
Producer: Thread-0は 8 を生産完了
Producer: Thread-0は 9 を生産完了
Consumer: Thread-1は 8 を消費中
Producer: Thread-0は 10 を生産完了
Consumer: Thread-1は 9 を消費中
Consumer: Thread-1は 10 を消費中
Thread-1 wait: データを待つ
Producer: Thread-0は 11 を生産完了
Consumer: Thread-1は 11 を消費中
Thread-1 wait: データを待つ
Producer: Thread-0は 12 を生産完了
Consumer: Thread-1は 12 を消費中
Producer: Thread-0は 13 を生産完了
Consumer: Thread-1は 13 を消費中
Thread-1 wait: データを待つ
Producer: Thread-0は 14 を生産完了
Consumer: Thread-1は 14 を消費中
Producer: Thread-0は 15 を生産完了
Consumer: Thread-1は 15 を消費中
Producer: Thread-0は 16 を生産完了
Consumer: Thread-1は 16 を消費中
Producer: Thread-0は 17 を生産完了
Consumer: Thread-1は 17 を消費中
Thread-1 wait: データを待つ
Producer: Thread-0は 18 を生産完了
Consumer: Thread-1は 18 を消費中
Thread-1 wait: データを待つ
Producer: Thread-0は 19 を生産完了
Consumer: Thread-1は 19 を消費中
Thread-1 wait: データを待つ
Producer: Thread-0は 20 を生産完了
Consumer: Thread-1は 20 を消費中
Thread-1 wait: データを待つ
Producer: Thread-0は 21 を生産完了