スレッド基礎

スレッドライフサイクル

  flowchart TB
	n((New)) ==> |start()|R{{Runnable}}
	R <--> |スケジューラ / yield|Ring{{Running}}
	Ring ==> |run() 完了|d((Dead))

	Ring ==> |ブロッキングイベント: IO, join, sleep...| Bl{{Blocked}}
	Bl ==> |ブロック解除| R

	Ring ==> |ロックを持つスレッドが wait() を呼び出す|WP{{Wait Pool}}
	WP ==> |notify / notifyAll / interrupt| LP{{Lock Pool}}
	LP ==>|ロック取得| R

スレッドの作成方法

Thread クラスを継承

ExtendsThread.java
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread extends Thread - run()");
    }
}

public static void main(String[] args) {
    MyThread t = new MyThread();
    t.start();
}

Runnable インターフェースを実装

ImplementsRunnable.java
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Thread implements Runnable - run()");
    }
}

public static void main(String[] args) {
    Thread t = new Thread(new MyRunnable(), "t1");
    t.start();
}

Callable インターフェースを実装

  • もどあり
  • 例外れいがいをスローできる
ImplementsCallable.java
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("Thread implements Callable - call()");
        return "Hello Callable";
    }
}

public static void main(String[] args) throws Exception {
    FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
    Thread t = new Thread(futureTask, "t1");
    t.start();
    System.out.println("戻り値: " + futureTask.get());
}

スレッドプールを使用

UseThreadPool.java
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> {
    System.out.println("Task running in thread pool");
});
executor.shutdown();

Runnable vs Callable

特性とくせいRunnableCallable
メソッドrun()call()
もどvoid総称型ジェネリック V
例外れいがい処理しょりchecked exception をスローできないException をスローできる
JDK バージョン1.01.5
  classDiagram
	class FutureTask{
		Callable
	}
	class Callable
	<<interface>> Callable
	class Runnable
	<<interface>> Runnable

	Callable --o FutureTask
	Runnable <|.. FutureTask

デーモンスレッド (Daemon Thread)

デーモンスレッドはほかのスレッドにサービスを提供ていきょうする特殊とくしゅなスレッドで、バックグラウンドでシステムサービス(GC など)を実行じっこうする。

システムにデーモンスレッドしかのこっていない場合ばあい、JVM は自動的じどうてき終了しゅうりょうする。
DaemonDemo.java
public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        System.out.println(Thread.currentThread().getName() + " working, " +
                (Thread.currentThread().isDaemon() ? "Daemon Thread" : "User Thread"));
        while (true) {}
    }, "t1");

    t1.setDaemon(true);  // デーモンスレッドに設定
    t1.start();

    Thread.sleep(Duration.ofSeconds(3));

    System.out.println(Thread.currentThread().getName() + " --- main thread ends");
}
// Output:
// t1 working, Daemon Thread
// main --- main thread ends
// Process finished with exit code 0
UserThreadDemo.java
public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        System.out.println(Thread.currentThread().getName() + " working, " +
                (Thread.currentThread().isDaemon() ? "Daemon Thread" : "User Thread"));
        while (true) {}
    }, "t1");

    t1.start();  // デフォルトは User Thread

    Thread.sleep(Duration.ofSeconds(3));

    System.out.println(Thread.currentThread().getName() + " --- main thread ends");
}
// Output:
// t1 working, User Thread
// main --- main thread ends
// (t1 がまだ実行中のためプログラムは終了しない)

ThreadLocal

ThreadLocal はかくスレッドに独立どくりつした変数へんすうのコピーを提供ていきょうし、共有きょうゆう状態じょうたい問題もんだい回避かいひする。

ビジネスシーン関連かんれんフレームワーク
Web アプリのセッション管理かんりSpring Session, Servlet HttpSession
ログトレースLogback, Slf4j, Log4j (MDC)
DB 接続せつぞく/トランザクション管理かんりSpring Data, Hibernate, MyBatis
スレッドプールでのコンテキスト伝達でんたつSpring Framework
スレッドプールを使用しようするさいは、タスク終了しゅうりょうに ThreadLocal をかならずクリアし、メモリリークを回避かいひすること。

スレッド間通信

wait / sleep の違い

特性とくせいwait()sleep()
所属しょぞくクラスObjectThread
ロック解放かいほうするしない
使用しよう条件じょうけんsynchronized ブロックないのみどこでも
起床きしょう方法ほうほうnotify() / notifyAll()時間じかん経過けいか自動じどう起床きしょう
Spurious Wakeup(にせ起床きしょうwait()if ではなく while ループと一緒いっしょ使用しようし、にせ起床きしょう回避かいひすること。

wait / notify 例

WaitNotifyDemo.java
synchronized (lock) {
    while (!condition) {  // while を使用して偽の起床を回避
        lock.wait();
    }
    // 処理ロジック
}

// 別のスレッド
synchronized (lock) {
    condition = true;
    lock.notifyAll();
}

参考リソース