ScheduledThreadPoolExecutorとTimerによるタイマーの実装の違い

# Timerの実装

```java
java.util.Timer
```
でタイマーを実装することができますが、ほかにも

```java
java.util.concurrent.Executors;
java.util.concurrent.ScheduledExecutorService;
```
を使って実装することも可能です。

# 記事の目的
タイマーで次のように10ms間隔でタスクを定期実行するよう設定した場合、

```java:TimerTest.java
import java.util.Timer;

public class TimerTest {

    public static void main(String[] args) {
        // Timerクラスのオブジェクトを作成
        Timer time = new Timer();
        time.scheduleAtFixedRate(new SampleTask(), 0, 10);
    }
}
```

```java:SampleTask.java
import java.util.TimerTask;

public class SampleTask extends TimerTask {
    public long before_time;

    public void run() {
        long now_time = System.currentTimeMillis();
        System.out.print("interval time[ms] ");
        System.out.println(now_time - before_time);
        before_time = now_time;
    }
}
```
結果

```
interval time[ms] 15
interval time[ms] 16
interval time[ms] 0
interval time[ms] 16
interval time[ms] 15
interval time[ms] 0
interval time[ms] 16
interval time[ms] 16
interval time[ms] 0
interval time[ms] 16
interval time[ms] 0
interval time[ms] 16
interval time[ms] 16
interval time[ms] 0
interval time[ms] 16
interval time[ms] 16
interval time[ms] 0
interval time[ms] 15
interval time[ms] 0
interval time[ms] 16
interval time[ms] 16
interval time[ms] 0
interval time[ms] 15
interval time[ms] 15
interval time[ms] 0
interval time[ms] 15
```
インターバル間隔は15msや0msなど**不安定です**。
ここが、大きな問題です。

ちなみに

```
Timer.scheduleAtFixedRate
```
でタスクを設定した場合、タスクがインターバル間隔以上に時間がかかったりすると、すぐ実行してしまったりします。

https://qiita.com/opengl-8080/items/ee8e926cf75e4d6058a2

こちらの方の説明が非常にわかりやすいです。

```
Executor.scheduleWithFixedDelay
```
を使用すれば固定間隔で使用できます。


# ScheduledExecutorService: サンプルコード

```java:ScheduleThread.java
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThread {

    public static void main(String[] args){

        ScheduledExecutorService executor = Executors.newScheduledThreadPool(15);
        ScheduledWork work1 = new ScheduledWork();

        //executor.scheduleAtFixedRate(work1, 0,1, TimeUnit.MILLISECONDS);
        executor.scheduleWithFixedDelay(work1, 0,100, TimeUnit.MICROSECONDS);
    }

}
```

```java:ScheduledWork.java
public class ScheduledWork implements Runnable{

    public long before_time;

    @Override
    public void run(){
        long now_time = System.currentTimeMillis();
        System.out.println(now_time - before_time);
        before_time = now_time;

    }

}

```
## コードの説明
定期実行するインスタンスはインターフェースRunnnableを継承します。Runnnableはrunメソッドを持つクラスのことです。
Executerとは

https://docs.oracle.com/javase/jp/8/docs/api/java/util/concurrent/Executor.html

この中で、

```
newScheduledThreadPool
```

の説明は

```
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
指定された遅延時間後、または周期的にコマンドの実行をスケジュールできる、スレッド・プールを作成します。
パラメータ:
corePoolSize - アイドルであってもプール内に維持されるスレッドの数。
戻り値:
新規生成されたスケジュール済みのスレッド・プール
例外:
IllegalArgumentException - corePoolSize < 0の場合
```
こちらとなります。
私の理解では、タスク=Executorです

https://runebook.dev/ja/docs/openjdk/java.base/java/util/concurrent/scheduledthreadpoolexecutor

こちらも参考になります。

# サンプルコードと結果について
WithFixedDelayで100usで設定した場合、

```
interval time[ms] 16
interval time[ms] 16
interval time[ms] 0
interval time[ms] 15
interval time[ms] 17
interval time[ms] 15
interval time[ms] 0
interval time[ms] 0
interval time[ms] 16
interval time[ms] 15
interval time[ms] 15
interval time[ms] 16
interval time[ms] 15
interval time[ms] 16
interval time[ms] 15
interval time[ms] 16
interval time[ms] 15
interval time[ms] 15
interval time[ms] 16
interval time[ms] 16
interval time[ms] 16
interval time[ms] 16
interval time[ms] 16
interval time[ms] 16
interval time[ms] 15
interval time[ms] 15
interval time[ms] 16
interval time[ms] 16
interval time[ms] 15
interval time[ms] 15
interval time[ms] 16
interval time[ms] 16
interval time[ms] 15
interval time[ms] 16
interval time[ms] 15
interval time[ms] 16
interval time[ms] 15
interval time[ms] 15
interval time[ms] 1
interval time[ms] 15
interval time[ms] 0
interval time[ms] 16
interval time[ms] 16
interval time[ms] 15
interval time[ms] 16
interval time[ms] 15
interval time[ms] 16
interval time[ms] 16
interval time[ms] 16
```
こんな感じで結局15msほどで反復実行します。
また、0msなどもあり不安定です。

intervalを30msにすると
```
interval time[ms] 32
interval time[ms] 31
interval time[ms] 31
interval time[ms] 31
interval time[ms] 31
interval time[ms] 31
interval time[ms] 31
interval time[ms] 30
interval time[ms] 33
interval time[ms] 31
interval time[ms] 32
interval time[ms] 31
interval time[ms] 32
interval time[ms] 32
interval time[ms] 30
interval time[ms] 32
interval time[ms] 31
interval time[ms] 31
interval time[ms] 31
interval time[ms] 31
interval time[ms] 31
interval time[ms] 31
interval time[ms] 32
interval time[ms] 31
interval time[ms] 30
interval time[ms] 31
interval time[ms] 45
interval time[ms] 46
interval time[ms] 45
interval time[ms] 30
interval time[ms] 31
interval time[ms] 31
interval time[ms] 32
interval time[ms] 30
interval time[ms] 46
interval time[ms] 31
interval time[ms] 46
interval time[ms] 31
interval time[ms] 30
interval time[ms] 31
interval time[ms] 31
interval time[ms] 31
```
30~46msとこれでも微妙に遅かったりちょうどよかったりします。

# まとめ
TimerもScheduledExecutorServiceどちらで実装しても、差はありません。
インターバル間隔がms単位の場合、15ms単位の遅れが突如混ざる可能性はどちらでもありえます。