タイミングタスクTimer/timerTaskとScheduledExecutorService

12259 ワード

我々のプログラミング過程で簡単なタイミングタスクを実行する必要があり,複雑な制御を行う必要がない場合,JDKにおけるTimerタイミングタスクを用いて実現することを考慮することができる.以下、LZは、その原理、インスタンス、およびTimer欠陥の3つの態様についてjava Timerタイマを解析する.
一、紹介
Javaでの完全なタイミングタスクは、Timer、TimerTaskの2つのクラスが協力して完了する必要があります.APIでは、後でバックグラウンドスレッドで実行されるタスクをスケジュールするツールです.タスクを1回実行するか、定期的に繰り返し実行するように手配できます.TimerTask:Timerによって、1回または繰り返し実行されるタスクとしてスケジュールされます.Timerは、バックグラウンドスレッド計画で指定されたタスクを実行するためのタイマツールであり、TimerTaskは、Timer計画可能なタスクを表す抽象クラスであることを理解することができます.
Timerクラス
ツールクラスTimerでは、4つの構築方法が提供されており、各構築方法はタイマスレッドを起動するとともに、Timerクラスは、外部同期を必要とせずに複数のスレッドが単一のTimerオブジェクトを共有できることを保証するため、Timerクラスはスレッドが安全である.しかし、各Timerオブジェクトは単一のバックグラウンドスレッドに対応しているため、すべてのタイマタスクを順番に実行するために使用されます.一般的に、スレッドタスクの実行にかかる時間は非常に短いはずですが、特殊な場合、あるタイマタスクの実行時間が長すぎると、タイマのタスク実行スレッドを「独占」します.その後のすべてのスレッドは、実行が完了するのを待たなければなりません.これにより、後続のタスクの実行が遅延し、これらのタスクが積み上げられます.具体的には、後で分析します.
プログラムの初期化がTimerを完了すると、タイミングタスクは私たちが設定した時間に従って実行され、Timerはschedule方法を提供し、この方法は複数の中重荷重方式で異なる状況に適応し、以下のようにします.
schedule(TimerTask task,Date time):指定した時間に指定したタスクを実行するように手配します.
schedule(TimerTask task,Date firstTime,long period):指定されたタスクが指定された時間に重複する固定遅延実行を開始するようにスケジュールされます.
schedule(TimerTask task,long delay):指定された遅延後に指定されたタスクを実行するように手配します.
schedule(TimerTask task,long delay,long period):指定されたタスクを指定された遅延から繰り返す固定遅延実行をスケジュールします.
同時にscheduleAtFixedRate法を再ロードし、scheduleAtFixedRate法はscheduleと同じであるが、彼らの側面の重点が異なり、後の分析を区別する.
scheduleAtFixedRate(TimerTask task,Date firstTime,long period):指定されたタスクを指定された時間から繰り返す固定レート実行をスケジュールします.
scheduleAtFixedRate(TimerTask task,long delay,long period):指定されたタスクを指定された遅延後に繰り返しの固定レート実行を開始するようにスケジュールします.
TimerTask
TimerTaskクラスは抽象クラスであり、Timerによって1回の実行または繰り返し実行のタスクとしてスケジュールされます.対応するタイマタスクが実行する操作を実行するための抽象的な方法run()メソッドがあります.したがって、各特定のタスククラスはTimerTaskを継承し、run()メソッドを書き換える必要があります.
また、抽象的でない2つの方法があります.
boolean cancel():このタイマタスクをキャンセルします.
long scheduledExecutionTime():このタスクが最近実際に実行されたスケジュール実行時間を返します.
二、実例
2.1、遅延時間を指定してタイミングタスクを実行する
public class TimerTest01 {  
    Timer timer;  
    public TimerTest01(int time){  
        timer = new Timer();  
        timer.schedule(new TimerTaskTest01(), time * 1000);  
    }  
      
    public static void main(String[] args) {  
        System.out.println("timer begin....");  
        new TimerTest01(3);  
    }  
}  
  
public class TimerTaskTest01 extends TimerTask{  
  
    public void run() {  
        System.out.println("Time's up!!!!");  
    }  
}

実行結果:
    :timer begin....  
  
3    :Time's up!!!!

2.2、指定時間にタイミングタスクを実行する
public class TimerTest02 {  
    Timer timer;  
      
    public TimerTest02(){  
        Date time = getTime();  
        System.out.println("    time=" + time);  
        timer = new Timer();  
        timer.schedule(new TimerTaskTest02(), time);  
    }  
      
    public Date getTime(){  
        Calendar calendar = Calendar.getInstance();  
        calendar.set(Calendar.HOUR_OF_DAY, 11);  
        calendar.set(Calendar.MINUTE, 39);  
        calendar.set(Calendar.SECOND, 00);  
        Date time = calendar.getTime();  
          
        return time;  
    }  
      
    public static void main(String[] args) {  
        new TimerTest02();  
    }  
}  
  
public class TimerTaskTest02 extends TimerTask{  
  
    @Override  
    public void run() {  
        System.out.println("          ...");  
    }  
}

時間が11:39:00に達するとスレッドタスクが実行され、もちろんその時間よりも大きく実行されます!!実行結果:
    time=Tue Jun 10 11:39:00 CST 2014  
          ...

2.3、指定時間遅延後に所定の間隔でタイミングタスクを循環実行する
public class TimerTest03 {  
    Timer timer;  
      
    public TimerTest03(){  
        timer = new Timer();  
        timer.schedule(new TimerTaskTest03(), 1000, 2000);  
    }  
      
    public static void main(String[] args) {  
        new TimerTest03();  
    }  
}  
  
public class TimerTaskTest03 extends TimerTask{  
  
    @Override  
    public void run() {  
        Date date = new Date(this.scheduledExecutionTime());  
        System.out.println("           :" + date);  
    }  
}

実行結果:
           :Tue Jun 10 21:19:47 CST 2014  
           :Tue Jun 10 21:19:49 CST 2014  
           :Tue Jun 10 21:19:51 CST 2014  
           :Tue Jun 10 21:19:53 CST 2014  
           :Tue Jun 10 21:19:55 CST 2014  
           :Tue Jun 10 21:19:57 CST 2014  
.................

このスレッドタスクについては、タスクを停止しないと、彼はずっと実行します.
上記の3つの例について、LZは簡単に実証しただけで、scheduleAtFixedRateの方法の例も説明していませんが、実はこの方法はscheduleの方法と同じです!
2.4.scheduleとscheduleAtFixedRateの分析
      1、schedule(TimerTask task, Date time)、schedule(TimerTask task, long delay)
この2つの方法では、指定された計画実行時間s c h e d u l edExecutionTime<=systemCurrentTimeの場合、taskはすぐに実行されます.scheduledExecutionTimeは、あるtaskの過剰な実行によって変更されません.
      2、schedule(TimerTask task, Date firstTime, long period)、schedule(TimerTask task, long delay, long period)
この2つの方法は、前述の2つとは少し異なり、Timerのタイマタスクは、前のタスクの実行時間が長いため遅延します.この2つの方法では、実行されるtaskの計画時間は、前のtaskの実際の時間とともに変化する.すなわち、scheduledExecutionTime(n+1)=realExecutionTime(n)+periodTimeである.すなわち、n番目のtaskが何らかの事情で今回の実行時間過程を引き起こし、最終的にsystemCurrentTime>=scheduledExecutionTime(n+1)となる場合、n+1番目のtaskはその時点で実行されず、n番目のtaskが実行されてから実行されるのを待つ.これにより、n+2個の実行がscheduledExecutionTimeの再生変化、すなわちscheduledExecutionTime(n+2)=realExecutionTime(n+1)+periodTimeを実現することになる.したがって,この2つの方法は保存間隔時間の安定性をより重視している.
      3、scheduleAtFixedRate(TimerTask task, Date firstTime, long period)、scheduleAtFixedRate(TimerTask task, long delay, long period)
前にも述べたようにscheduleAtFixedRateはscheduleメソッドの側面とは異なり,scheduleメソッドは間隔時間の安定化を保存することに重点を置き,scheduleAtFixedRateメソッドは実行周波数の安定化を維持することに重点を置いている.なぜかというと、原因は以下の通りです.scheduleメソッドでは前のタスクの遅延により後のタイミングタスクが遅延しますが、scheduleAtFixedRateメソッドではできません.n番目のtaskの実行時間が長すぎるとsystemCurrentTime>=scheduledExecutionTime(n+1)となり、彼が直ちにn+1番目のtaskを実行するのを待つことはありません.したがって、scheduleAtFixedRateメソッドの実行時間の計算方法はscheduleとは異なり、scheduledExecutionTime(n)=firstExecuteTime+n*periodTimeとなり、この計算方法は永遠に変わらない.したがって、scheduleAtFixedRateは、実行周波数の安定性を維持することに重点を置いている.
三、Timerの欠陥
3.1、Timerの欠陥
Timerタイマは、タイミング(指定時間実行タスク)、遅延(5秒遅延実行タスク)、周期的にタスクを実行できる(1秒ごとにタスクが実行される)が、Timerにはいくつかの欠陥がある.まず、Timerのスケジューリングに対するサポートは、相対時間ではなく絶対時間に基づいているため、システム時間の変化に非常に敏感である.次に、Timerスレッドは異常をキャプチャせず、TimerTaskがチェックされていない異常を放出すると、Timerスレッドが終了し、Timerも再回復しないスレッドの実行は、Timerスレッド全体がキャンセルされると勘違いします.また、すでにスケジュールされていないTimerTaskも実行されず、新しいタスクもスケジュールされません.したがって、TimerTaskが未検査の異常を投げ出すと、Timerは予想できない行為を生じる.
1、Timer管理時間遅延欠陥
前のTimerでは、タイミングタスクを実行するときに1つのスレッドタスクしか作成されません.複数のスレッドが存在する場合、あるスレッドが何らかの理由でスレッドタスクの実行時間が長すぎ、2つのタスクの間隔時間を超えた場合、いくつかの欠陥が発生します.
public class TimerTest04 {  
    private Timer timer;  
    public long start;     
      
    public TimerTest04(){  
        this.timer = new Timer();  
        start = System.currentTimeMillis();  
    }  
      
    public void timerOne(){  
        timer.schedule(new TimerTask() {  
            public void run() {  
                System.out.println("timerOne invoked ,the time:" + (System.currentTimeMillis() - start));  
                try {  
                    Thread.sleep(4000);    //    3000  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        }, 1000);  
    }  
      
    public void timerTwo(){  
        timer.schedule(new TimerTask() {  
            public void run() {  
                System.out.println("timerOne invoked ,the time:" + (System.currentTimeMillis() - start));  
            }  
        }, 3000);  
    }  
      
    public static void main(String[] args) throws Exception {  
        TimerTest04 test = new TimerTest04();  
          
        test.timerOne();  
        test.timerTwo();  
    }  
}

通常の考え方では、timertwoは3 s後に実行されるべきで、その結果は次のようになります.
timerOne invoked ,the time:1001  
timerOne invoked ,the time:3001

しかし、期待に反して、timerOneはsleep(4000)のため、4 Sを休眠し、同時にTimer内部はスレッドであり、timeOneに必要な時間が間隔時間を超え、結果:
timerOne invoked ,the time:1000  
timerOne invoked ,the time:5000

2、Timerが異常欠陥を投げ出す
TimerTaskがRuntimeExceptionを放出すると、Timerはすべてのタスクの実行を終了します.次のようになります.
public class TimerTest04 {  
    private Timer timer;  
      
    public TimerTest04(){  
        this.timer = new Timer();  
    }  
      
    public void timerOne(){  
        timer.schedule(new TimerTask() {  
            public void run() {  
                throw new RuntimeException();  
            }  
        }, 1000);  
    }  
      
    public void timerTwo(){  
        timer.schedule(new TimerTask() {  
              
            public void run() {  
                System.out.println("       ??");  
            }  
        }, 1000);  
    }  
      
    public static void main(String[] args) {  
        TimerTest04 test = new TimerTest04();  
        test.timerOne();  
        test.timerTwo();  
    }  
}

実行結果:timerOneが例外を放出し、timerTwoタスクが終了しました.
Exception in thread "Timer-0" java.lang.RuntimeException  
    at com.chenssy.timer.TimerTest04$1.run(TimerTest04.java:25)  
    at java.util.TimerThread.mainLoop(Timer.java:555)  
    at java.util.TimerThread.run(Timer.java:505)

Timerの欠陥については,ScheduledThreadPoolExecutorを代替することが考えられる.Timerは絶対時間に基づいており、システム時間に敏感であるが、ScheduledThreadPoolExecutorは相対時間に基づいている.Timerは内部が単一スレッドであり、ScheduledThreadPoolExecutorは内部がスレッドプールであるため、複数のタスクの同時実行をサポートできます.
3.2、Timerの代わりにScheduledExecutorServiceを使用する
1、問題解決一:
public class ScheduledExecutorTest {  
    private  ScheduledExecutorService scheduExec;  
      
    public long start;  
      
    ScheduledExecutorTest(){  
        this.scheduExec =  Executors.newScheduledThreadPool(2);    
        this.start = System.currentTimeMillis();  
    }  
      
    public void timerOne(){  
        scheduExec.schedule(new Runnable() {  
            public void run() {  
                System.out.println("timerOne,the time:" + (System.currentTimeMillis() - start));  
                try {  
                    Thread.sleep(4000);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        },1000,TimeUnit.MILLISECONDS);  
    }  
      
    public void timerTwo(){  
        scheduExec.schedule(new Runnable() {  
            public void run() {  
                System.out.println("timerTwo,the time:" + (System.currentTimeMillis() - start));  
            }  
        },2000,TimeUnit.MILLISECONDS);  
    }  
      
    public static void main(String[] args) {  
        ScheduledExecutorTest test = new ScheduledExecutorTest();  
        test.timerOne();  
        test.timerTwo();  
    }  
}

実行結果:
timerOne,the time:1003  
timerTwo,the time:2005

2、問題解決2
public class ScheduledExecutorTest {  
    private  ScheduledExecutorService scheduExec;  
      
    public long start;  
      
    ScheduledExecutorTest(){  
        this.scheduExec =  Executors.newScheduledThreadPool(2);    
        this.start = System.currentTimeMillis();  
    }  
      
    public void timerOne(){  
        scheduExec.schedule(new Runnable() {  
            public void run() {  
                throw new RuntimeException();  
            }  
        },1000,TimeUnit.MILLISECONDS);  
    }  
      
    public void timerTwo(){  
        scheduExec.scheduleAtFixedRate(new Runnable() {  
            public void run() {  
                System.out.println("timerTwo invoked .....");  
            }  
        },2000,500,TimeUnit.MILLISECONDS);  
    }  
      
    public static void main(String[] args) {  
        ScheduledExecutorTest test = new ScheduledExecutorTest();  
        test.timerOne();  
        test.timerTwo();  
    }  
}

実行結果:
timerTwo invoked .....  
timerTwo invoked .....  
timerTwo invoked .....  
timerTwo invoked .....  
timerTwo invoked .....  
timerTwo invoked .....  
timerTwo invoked .....  
timerTwo invoked .....  
timerTwo invoked .....  
........................