雑に理解するJavaのスレッド

Javaで非同期処理や並列処理を実行するにはスレッドを扱う必要があります。
この記事は、Java初〜中級者がスレッドについてざっくり理解できるようになるといいなあという感じで書いております。

# スレッドとはなんぞや

スレッドとは、プログラムの実行時にたどる道筋のようなもの。
たとえば

```java
  public static void main(String[] args) {
    run();
  }

  static void run() {
    int result = sum(1, 2);
    System.out.println(result);
  }

  static int sum(int a, int b) {
    return a + b;
  }
```

というプログラムを実行すると、
main()が呼ばれて、run()が呼ばれて、sum()が呼ばれて、System.out.println()が呼ばれて、、、のように処理を一つずつ実行しながらたどっていく。
この一筋の道をたどりながら処理を実行していくやつがスレッドです。


## すべての処理はスレッドの上で動く

例えばmain関数から処理を実行すると...

```java
  public static void main(String[] args) {
    Thread currentThread = Thread.currentThread(); // 自分自身のスレッドを取得
    System.out.printf("ID:%d, Name:%s, Priority:%d, State:%s%n",
        currentThread.getId(),
        currentThread.getName(),
        currentThread.getPriority(),
        currentThread.getState());
  }
```

実行結果

```
ID:1, Name:main, Priority:5, State:RUNNABLE
```

`main`という名前のスレッドで実行されたのがわかる

## 別スレッドでなにか処理を実行するコードを書いてみる

mainスレッドから別スレッドを分岐して非同期に何か処理を実行してみる。
`Thread`クラスをnewし、`start()`を呼ぶ。

```java
public class Sample {

  public static void main(String[] args) throws Exception {
    Thread thread = new Thread(() -> { // Threadを作成。コンストラクタに実行したい処理を渡す
      for (int i = 1; i < 5; i++) {
        System.out.println("thread: " + i);
        try {
          Thread.sleep(100L);
        } catch (InterruptedException e) {
          return;
        }
      }
    });

    System.out.println("main: 1");

    thread.start(); // スレッドの処理を開始

    System.out.println("main: 2");
    Thread.sleep(150L);
    System.out.println("main: 3");

    thread.join(); // スレッドの終了を待機（待たない場合はやらないでも特に問題ない）
    System.out.println("main: 4");
  }
}
```

出力結果

```
main: 1
main: 2
thread: 1
thread: 2
main: 3
thread: 3
thread: 4
main: 4
```

mainスレッドの処理と生成したスレッドの処理はそれぞれ独立して動いているのがわかる


# スレッドはどんな情報を持っているか

Javaでは`Thread`クラスを通してスレッドを扱う。
IntellijでThreadクラスのプロパティを見るとこんな感じ

<img width="517" alt="Screen Shot 2018-12-17 at 11.40.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/109747/813760df-9cf4-29a5-ce6b-376b635f69fc.png">

よく見かけるものだけ説明します。

### ID :long

スレッド生成時に自動的に割り振られるlong型の値。一意で不変。

### name :String

スレッドの名前。
`setName(String)`で任意の名前を設定可能。
`new Thread()`とかでスレッドを作ると`Thread-1`、`Thread-2`みたいな名前が勝手に設定される

### state :Thread.State

スレッドの状態。「実行中」とか「待機中」とか。
スレッドは状態を持っていて、`NEW`,`RUNNABLE`,`BLOCKED`,`WAITING`,`TIMED_WAITING`,`TERMINATED`のいずれかの状態にある。

詳細は[Javadoc](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Thread.State.html)参照

### interrupted :boolean

スレッドが中断されたかどうか。

- `Thread#interrupt()`でこの値をtrueにすることができる
- `Thread#isInterrupted()`もしくは`Thread.interrupted()`で中断されたかどうかを取得できる（違いは後ほど説明）

※ `Thread#interrupt()`を呼んでも実行中のスレッドが直ちに停止するわけではない。このメソッドは`interrupted`というフィールドをtrueに設定するだけ。(これも後ほど説明)

### priority :int

スレッドの優先度。
資源が競合した際、高い優先度を持つスレッドの方が優先的に実行される。
デフォルトは5(`Thread.NORM_PRIORITY`)で、最低が1(`Thread.MIN_PRIORITY`)で、最高が10(`Thread.MAX_PRIORITY`)。
`setPriority(int)`で変更可能。

### daemon: boolean

デーモンスレッドかどうか。
`daemon`が`true`のものをデーモンスレッド、`false`のものはユーザースレッドと呼ぶ。
デフォルト`false`で、`setDaemon(boolean)`で設定可能。

※Javaのプロセスは全てのユーザースレッドが終了すると自動的に終了する
つまり、デーモンスレッドが何か実行中でも、完了を待たずにプロセスは終了する。


# いろいろなスレッドプログラミング

別スレッドで何か実行するプログラムを書く方法はいろいろある。

## 生のThreadを使う方法

`Thread`のコンストラクタ引数に実行する処理をラムダとかで入れてnewし、`start()`を呼ぶことでスレッドが開始される。

```java
Thread thread = new Thread(() -> {
    // なにかの処理...
});
thread.start(); // スレッドを開始。「なにかの処理」が非同期に実行される
```

`Thread`を継承し`run`をオーバーライドしてもよい。
(が、個人的には↑の方法のほうがおすすめです。処理が`Thread`クラスに依存してしまうので。)

```java
class MyThread extends Thread {
  @Override
  public void run() {
    // なにかの処理...
  }
}

MyThread thread = new MyThread();
thread.start(); // スレッドを開始。「なにかの処理」が非同期に実行される
```


## Executorを使う方法

簡単にスレッドプールみたいなものが作成できる。

```java
ExecutorService threadPool = Executors.newFixedThreadPool(3); // スレッド数=3のスレッドプールを作成

// 100個処理を投入してみる
for (int i = 0; i < 100; i++) {
  threadPool.submit(() -> { // threadPoolは3個のスレッドを駆使して投入された処理をどんどん実行していく
    // なにかの処理...
  });
}

// submitされたすべての処理が終了するのを待機
threadPool.awaitTermination(1, TimeUnit.MINUTES);

// スレッドプールを停止
threadPool.shutdown();
```

`Executors.newFixedThreadPool()`以外にもいろいろある。

- `newCachedThreadPool()`
- `newFixedThreadPool()`
- `newScheduledThreadPool()`
- `newSingleThreadExecutor()`
- `newWorkStealingPool()`


## CompletableFutureを使う方法

JavaScriptで言うところのFuture/Promise的なもの。
内部的にはスレッド制御にExecutorが使用されている


```java
// 処理を非同期に実行する
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
  // なにかの処理...
});

// 戻り値のCompletableFutureにコールバックを登録できる
future.whenComplete((aVoid, throwable) -> { // JavaScriptのpromiseで言うところの.then()
  // 「なにかの処理」が終わったらここが呼ばれる
  // - 「なにかの処理」が正常に完了した場合、引数のthrowableはnull
  // - 「なにかの処理」で例外が発生した場合、引数のthrowableにその例外が渡される
});
```

# スレッドの中断



## 中断命令を出す

実行中のスレッドの処理を中断させたいときは、`Thread#interrupt()`を使う

```java
Thread thread = createNanikanoThread();
thread.start(); // スレッドを動かす
...
thread.interrupt(); // 動いているスレッドに対して中断命令を送る
```

だがしかし、`interrupt()`したからといって、対象のスレッドがすぐに止まるとは限らないッ！
このメソッドを呼ぶと何が行われるかというと、
対象スレッドが持つ`interrupted`という名前のbooleanフィールドに`true`がセットされるだけのイメージ（←多少乱暴な説明）。
基本的には中断される側の処理でこの`interrupted`フラグを確認しなければならない。

## 中断状態の確認

中断（interrupt）される可能性のある処理は、それを想定した実装をしなければならない。
中断されたかどうか（`interrupted`フラグがONになっているかどうか）は`Thread.interrupted()`で確認できる。

```java
// ずっと何かをする処理
while (true) {
  if (Thread.interrupted()) { // 中断されたかどうか確認する（※このメソッドを呼ぶとinterruptedフラグはfalseに戻る）
    throw new InterruptedException(); // 例外を投げて処理を抜ける。基本的にはInterruptedExceptionを使えばOK
  }
  // 何かの処理
  ...
}
```

`Thread.interrupted()`を呼んで状態を確認すると、interruptedフラグはfalseにリセットされる。
（ちなみに`Thread.currentThread().isInterrupted()`で確認すればリセットされない）


メソッドによっては`interrupted`状態の確認をやってくれるようなものもある。
たとえば`Thread.sleep()`は、スリープ中に`interrupted`フラグが立ったらスリープをやめて`InterruptedException`をスローしてくれる。
（ちなみにこのときにもinterruptedフラグはfalseにリセットされる）

```java
// ずっと何かをする処理
while (true) {
  Thread.sleep(1000L); // 処理を1秒間休止。休止中にinterruptされたらInterruptedExceptionがスローされる
  ...
}
```

## InterruptedExceptionの扱い

InterruptedExceptionがスローされたということは、スレッドが中断されました〜という情報を受け取ったということ。
この情報を失わせずに適切な処理をしなければならない。

### だめな例1: 中断命令を無視して処理を続行してしまう

スレッドが中断されましたよ〜という情報を無視して続行しちゃっている

```java
try {
  Thread.sleep(100);
} catch (InterruptedException ex) {
}
```

### だめな例2: 別の例外でラップしてスローする

よく見かけるやつ

```java
try {
  Thread.sleep(100);
} catch (InterruptedException ex) {
  throw new RuntimeException(ex);
}
```

スレッドが中断されましたよ〜という情報が失われてしまっている。
外側の処理でこの例外をcatchしたときに、予期しないエラーとして扱われてしまう。

### 悪くない例: interruptedフラグを再びONにしてから別の例外でラップしてスローする

```java
try {
  Thread.sleep(100);
} catch (InterruptedException ex) {
  Thread.currentThread().interrupt(); // Here!
  throw new RuntimeException(ex);
}
```

interruptedフラグの判定を外側の処理に任せる。
中断されたよ〜という情報が生き残る。
もちろん外側でこれをキャッチする処理は、interruptedされたかどうか判断して何かしなきゃいけない

### 個人的に編み出したプラクティス: 専用の非検査例外を作る

(この方法がいいのかどうかはわからない。もっといい方法あったら教えてください...)

```java
try {
  Thread.sleep(100);
} catch (InterruptedException ex) {
  throw new InterruptedRuntimeException(ex);
}
```

外側の処理で必ずこのInterruptedRuntimeExceptionをキャッチして適切な処理をする。

sleepするときにいちいちtry-catchするのも面倒なのでユーティリティクラスを作っています

```java
public class ThreadUtils {

  /**
   * @param n スリープする時間（ミリ秒）
   * @throws InterruptedRuntimeException スレッドが中断されていた場合
   */
  public static void sleep(long n) {
    try {
      Thread.sleep(n);
    } catch (InterruptedException ex) {
      throw new InterruptedRuntimeException(ex);
    }
  }

  /**
   * 現在のスレッドが中断されたかチェックする.
   * @throws InterruptedRuntimeException スレッドが中断されていた場合
   */
  public static void checkForInterruption() {
    if (Thread.interrupted()) {
      throw new InterruptedRuntimeException(ex);
    }
  }
}
```


# スタックトレースについて

WIP