スレッドの状態
- start()メソッドが呼ばれると実行可能状態(スレッドが作成され、実行を待っている)に移る
- スレッドが実行される実行状態に次に移る。このときrun()メソッドが実行される
- run()メソッドが終わり、終了する
※一つのスレッドに対して2回以上start()メソッドは実行できない→IllegalThreadStateExceptionが発生する
スレッドの優先度
- スレッドには実行の優先順番が存在し、getPriority()メソッドで取得できる
- setPriority()メソッドで優先順位を変更できる
スレッド制御
- sleep()メソッド→このメソッドを実行したスレッドはミリ秒休止する
- interrupt()メソッド→休止中のスレッドに割り込む
package cp8.no4;
public class Main {
public static void main(String[] args) {
Thread threadA = new Thread(() -> {
System.out.println("threadA : sleepの開始");
try {
// 5秒間ThreadAのスレッドを休止させる
Thread.sleep(5000);
System.out.println("threadA : sleep終了");
} catch (InterruptedException e) {
System.out.println("threadA : 割り込みキャッチ");
}
System.out.println("threadA : 処理再開");
});
threadA.start();
try {
System.out.println("main : sleepの開始");
// 3秒間メインスレッドを休止させる
Thread.sleep(3000);
System.out.println("main : sleep終了");
// threadAに割り込む
threadA.interrupt();
} catch (InterruptedException e) {
System.out.println("main : 割り込みキャッチ");
}
}
}
実行結果
main : sleepの開始
threadA : sleepの開始
main : sleep終了
threadA : 割り込みキャッチ
threadA : 処理再開
※スレッドの優先度によって実行順序が決定されるので、メインスレッドの実行結果がthreadAより先に表示されている。
排他制御と同期制御
- 排他制御→処理が行われている間は他のスレッドがこのオブジェクトを使えないようにする
- 同期制御→実行のタイミングを合わせる(それぞれのメソッドにおいて値をセットし終わってから表示するなど)
マルチスレッドで排他制御を行わない例
package cp8.no5;
public class Main {
public static void main(String[] args) {
Share share = new Share();
ThreadA threadA = new ThreadA(share);
ThreadB threadB = new ThreadB(share);
threadA.start();
threadB.start();
}
}
class Share {
private int a = 0;
private String b;
public void set() {
a++;
b = "test";
System.out.println("set() a : " + a + " b : " + b);
}
public void print() {
a--;
b = null;
System.out.println("print() a : " + a + " b : " + b);
}
}
class ThreadA extends Thread {
private Share share;
public ThreadA(Share share) {
this.share = share;
}
public void run() {
for (int i = 0; i < 5; i++) {
share.set();
}
}
}
class ThreadB extends Thread {
private Share share;
public ThreadB(Share share) {
this.share = share;
}
public void run() {
for (int i = 0; i < 5; i++) {
share.print();
}
}
}
実行結果
set() a : 1 b : test
print() a : 0 b : null
set() a : 1 b : test
set() a : 1 b : test
set() a : 2 b : test
set() a : 3 b : test
print() a : 0 b : null
print() a : 2 b : null
print() a : 1 b : null
print() a : 0 b : null
※値をセットするメソッドと表示するメソッドの順番が整列されていない
排他制御(synchronized)
- 複数のスレッドで同時実行したくないときに使用する
- 排他制御を実現できる
- 同時に1つのスレッドからしか実行できないことを保証できる
- 実行している間共有オブジェクトはロック状態になる
同期制御
- wait()→現在のスレッドを待機させる
- notify()→待機中のスレッドを1つ再開させる
- notifyAll()→待機中のスレッドを全て再開させる
排他制御、同期制御を行った例
package cp8.no6;
// 共有されるオブジェクト
class Share {
private int a = 0;
private String b;
public synchronized void set() {
while (a != 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
notify();
a++;
b = "test";
System.out.println("set() a : " + a + " b : " + b);
}
public synchronized void print() {
while (b == null) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
notify();
a--;
b = null;
System.out.println("print() a : " + a + " b : " + b);
}
}
class ThreadA extends Thread {
private Share share;
public ThreadA(Share share) {
this.share = share;
}
public void run() {
for (int i = 0; i < 5; i++) {
share.set();
}
}
}
class ThreadB extends Thread {
private Share share;
public ThreadB(Share share) {
this.share = share;
}
public void run() {
for (int i = 0; i < 5; i++) {
share.print();
}
}
}
public class Main {
public static void main(String[] args) {
Share share = new Share();
ThreadA threadA = new ThreadA(share);
ThreadB threadB = new ThreadB(share);
threadA.start();
threadB.start();
}
}
実行結果
set() a : 1 b : test
print() a : 0 b : null
set() a : 1 b : test
print() a : 0 b : null
set() a : 1 b : test
print() a : 0 b : null
set() a : 1 b : test
print() a : 0 b : null
set() a : 1 b : test
print() a : 0 b : null
※synchronized指定されていないメソッドやロック状態のオブジェクトの取得や未開放状態においてnotifyメソッドを呼ぶとillegalMonitorStateExceptionが発生する
競合状態
- デッドロック→すべてのスレッドが待機状態でnotify()メソッドを呼べない状態
- ライブロック→共有資源の獲得の際に他のスレッドからロックされている状態
- スレッドスタベーション→共有オブジェクトが他のスレッドに占有され長時間待たされる状態
ふりかえり
- マルチスレッド実行時にはスレッドの優先度に従ってメソッドが実行される
- 排他制御、同期制御を正しく実装することでマルチスレッド配下においても実行順序を決めることができる