Java Programming Thread Runnable (スレッド)


# Thread とは
**Thread (スレッド)** とは <font color="orange">処理を実行する流れの単位</font> を示す。
パソコン操作についてまとめるなら、

<div align="center">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259737/bbbc717e-1ac8-5777-f5e5-8d3d9cd48c45.png">
</div>

この一連の処理がThread(スレッド)とイメージすると良い。
***


#シングルスレッド & マルチスレッド
シングルスレッド(左図): 1つの処理だけで終了するスレッド
マルチスレッド(右図): 複数のスレッドが同時に動くスレッド

![qiita01 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259737/da763636-0ba9-212a-4017-9a5e15a11b5e.png)

このようにイメージできる。
###マルチスレッドの利点
シングルスレッドよりも高速な処理が可能である。

For example)
Microsoft Wordで作業中、同時にスペルチェックが行われていたり,単語数を表示したりしている。
これがマルチスレッドの真骨頂である。
***

#Thread(スレッド)の基本操作
- クラスにRunnableインターフェースを実装する。

```java:Runnable.java
class クラス名 implements Runnable{
  public void run(){
    /* 処理を書く */
  }
}
```

- Threadのコンストラクタの引数としてRunnableを実装したクラスを渡し、宣言します。

```java:Thread.java
Thread thread = new Thread(クラス型変数);
```
Thread型のコンストラクタのリファレンスは以下。

```java:Reference-Thread-Constructor(リファレンス)
Thread
public Thread(Runnable target)
```

- Threadの起動
<font color="orange">start()メソッド</font>でクラス内のrun()メソッドを実行できます。

```java:ThreadStart.java
thread.start();
```

- Threadが終了したかを確認する
Threadの<font color="orange">join()メソッド</font>を使用します。  
*今回はThreadが配列で複数あると仮定します。

```java:Join.java
try{
    for(int i=0; i<thread.length; i++){
        thread[i].join();
    }
} catch(InterruptedException ie){
    ie.printStackTrace();
}
```

***
###スレッドありとなしの違い
変数sumに1を加算する。
これを1000000000回繰り返すプログラムについてスレッド有無の場合による実行時間の検証。
####スレッドなしの場合
```java:NoThread.java
import java.util.Date;
import java.math.BigInteger;

public class NoThread{
    private static BigInteger N = new BigInteger("1000000000");
    private static BigInteger sum = BigInteger.ZERO;

    public static void main(String[] args){
        long start_time = new Date().getTime();
        calculate();
        long end_time = new Date().getTime();
        output(start_time, end_time);
    }

    public static void calculate(){
        BigInteger i = BigInteger.ZERO;
        while(!i.equals(N)){
            sum = sum.add(BigInteger.ONE);   // sum += i
            i = i.add(BigInteger.ONE);       // i++
        }
    }

    public static void output(long s, long e){
        System.out.println("Sum: "+sum);
        System.out.println("total time: "+(e-s)+"ms");
    }
}
```

実行結果:
<img width="551" alt="スクリーンショット 2019-06-08 0.06.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259737/d5b0d941-dffb-e9bd-cfd6-97713ce5f128.png">

| Thread数 | 実行時間 |
|:-:|:-:|
| なし | 46371 ms|
*処理にかかる時間は常に一定ではない。

####スレッドありの場合
今回は実行時の引数にスレッド数を指定します。

```java:ThreadRun.java
import java.math.BigInteger;
import java.util.Date;

public class ThreadRun{
    private static BigInteger N = new BigInteger("1000000000");
    private static BigInteger sum = BigInteger.ZERO;

    public static void main(String[] args){
        int num_threads = Integer.parseInt(args[0]);
        BigInteger range = N.divide(new BigInteger(args[0]));

        ThreadRunnable[] tr = new ThreadRunnable[num_threads];
        Thread[] t = new Thread[num_threads];

        BigInteger low = BigInteger.ONE;
        BigInteger high = low.add(range);
        long start_time = new Date().getTime();

        for(int i=0; i<num_threads-1; i++){
            tr[i] = new ThreadRunnable(low, high);
            t[i] = new Thread(tr[i]);
            t[i].start();

            low = high.add(BigInteger.ONE);
            high = low.add(range);
        }
        tr[num_threads-1] = new ThreadRunnable(low, high);
        t[num_threads-1] = new Thread(tr[num_threads-1]);
        t[num_threads-1].start();

        try{ // check whether threads finished
            for(int i=0; i<num_threads; i++){
                t[i].join();
            }
        } catch(InterruptedException ie){
            ie.printStackTrace();
        }

        for(int i=0; i<num_threads; i++){
            sum = sum.add(tr[i].getSum());
        }
        
        long end_time = new Date().getTime();
        System.out.println("Sum: "+sum);
        System.out.println("total time: "+(end_time-start_time)+"ms");
    }

    protected static class ThreadRunnable implements Runnable{
        private BigInteger sum_temp;
        private BigInteger low, high;
        public ThreadRunnable(BigInteger l, BigInteger h){
            this.low = l;
            this.high = h;
            this.sum_temp = BigInteger.ZERO;
        }

        @Override
        public void run(){
            while(!low.equals(high)){
                sum_temp = sum_temp.add(BigInteger.ONE);  // sum_temp += low
                low = low.add(BigInteger.ONE); // low++;
            }
        }

        public BigInteger getSum(){
            return sum_temp;
        }
    }
}
```
実行結果:
<img width="543" alt="スクリーンショット 2019-06-08 0.07.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259737/095c497a-85b4-4c48-37cb-5a77a5465cb8.png">
Thread(スレッド)がある場合は、

| Thread数 | 実行時間 |
|:-:|:-:|
| 8 | 26840 ms |
| 16 | 26462 ms |
*処理にかかる時間は常に一定ではない。

***

#####まとめ
スレッドありの場合の方がスレッドなしよりも処理時間が短くなりました。
スレッドを有効活用することで大幅に時間を削減することができる。
だが、スレッドを増やしすぎてしまうと逆に実行時間が多くなるので<font color="orange">適切なスレッド数を指定する</font>のが重要。