1. 概要
Java でタスク実行のタイミングを計る機能を実装するには、主に Timer クラスと TimerTask クラスの 2 つのクラスが使用されます。このうち、Timer は、指定されたタスクを指定された計画に従ってバックグラウンド スレッド上で実行するために使用されます。
TimerTask は抽象クラスであり、そのサブクラスは Timer によってスケジュールできるタスクを表します。実行される特定のコードは、TimerTask に実装する必要がある run メソッドに記述されます。
2 番目に、最初に最も単純な例を見てみましょう
コードを通して説明します
import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TimerDemo { public static String getCurrentTime() { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(date); } public static void main(String[] args) throws InterruptedException { System.out.println("main start:"+getCurrentTime()); startTimer(); Thread.sleep(1000*5); //休眠5秒 System.out.println("main end:"+getCurrentTime()); } public static void startTimer(){ TimerTask task = new TimerTask() { @Override public void run() { System.out.println("task run:"+getCurrentTime()); } }; Timer timer = new Timer(); timer.schedule(task, 0); } }
印刷による情報の観察を容易にするために、メイン メソッドに印刷情報を追加し、Thread.sleep を呼び出してメインスレッドはしばらくスリープします。さらに、現在の日付を取得する getCurrentTime メソッドがクラスに追加されます。
上記のコードは、startTimer メソッドで、まず TimerTask オブジェクト (タイマーによって実行されるタスク) を作成し、次に Timer オブジェクトを作成してから、Timer クラスのスケジュール メソッドを呼び出します。 Timer クラスには、異なるパラメーターを持つ複数のスケジュール メソッドがあります。ここで使用されるのは:
public void schedule(TimerTask task, long delay)
このメソッドの意味は、タイマーがタスクを実行する前に遅延 (ミリ秒) を遅らせることです。遅延が負または 0 の場合、タスクはすぐに実行されます。また、これは 1 回限りの実行タスクであり、今後繰り返し実行 (またはスケジュール) されることはありません。
Timerクラスには以下のような同様の機能を持つメソッドも提供されています。
public void schedule(TimerTask task, Date time)
このメソッドと上記のメソッドの違いは、上記のメソッドは実行の遅延を指定しているのに対し、このメソッドは即時実行を指定していることです。特定の時点。システムの現在時刻がパラメータ time で指定された時刻を超えた場合、タスクは直ちに実行されることに注意してください。
上記のコードを実行すると、プログラムが次のような 2 つのメッセージをすぐに出力したことがわかりました:
main start:2016-01-13 22:23:18
task run:2016-01-13 22:23 :18
ここでスケジュール メソッドに渡した遅延パラメーターの値は 0 であるため、タスクはすぐに実行され、2 つのステートメントによって出力される時間は同じであり、これは本来あるべきものです。受信遅延値を自分で変更して、出力情報の変化を確認できます。約 5 秒後 (つまり、スリープ時間)、別のメッセージが出力されました:
main end:2016-01-13 22:23:23
メッセージが出力された時間は、上記のステートメントとは 5 秒異なりました一貫したスリープ設定も非常に合理的です。
しかし、非常に興味深い現象が見つかります。この時点では、待機中のタスクがない場合でも、メインスレッドが終了しないことがわかります。後で実行されると、タイマーで作成されたバックグラウンド スレッドはすぐには終了しません。関連する Java ドキュメント ドキュメントを確認し、タイマー スレッドはアクティブに終了せず、ガベージ コレクションを待機する必要があると説明しました。ただし、Java のガベージ コレクションの待機はコード自体では制御できず、仮想マシンによって制御されます。
少し調べた結果、Timer オブジェクトを作成して Timer timer = new Timer(); ステートメントを実行すると、タイマー スレッドが作成されることがわかりました。つまり、上記のコードに timer.schedule(task, 0); という文が含まれていない場合でも、プログラムは終了しません。これはかなり無理があるように感じます。 Timer クラスのソース コードを再度調べたところ、Boolean パラメーターを含むコンストラクターもあることを発見しました:
public Timer(boolean isDaemon)
パラメーター名からわかるように、パラメーター値が true の場合、Timer によって作成されたタイマー スレッドはデーモンです。糸。 。デーモン スレッドの意味は、Java プロセス内のすべての作業スレッドが終了すると、デーモン スレッドが自動的に終了することです。
現時点では、上記の例で Timer オブジェクトを作成するコードを次のように変更するだけです: Timer timer = new Timer(true);
プログラムの実行後、メイン スレッド (メインスレッドはデーモンスレッドではなく、作業スレッドです) を終了します。 その後、プログラムは終了します。これは、タイマースレッドも終了したことを意味します。これは、パラメーター true を追加した後、デーモンスレッドとして作成されることを意味します。
しかし、問題は、実際のアプリケーションのシナリオでは、多数のワーカー スレッドが実行されており、プログラムが簡単に終了しないことです。では、タイマーをすぐに終了または閉じたい場合はどうすればよいでしょうか?以下に紹介していきます。
3. タイマーの終了
Timer クラスには、タイマーをキャンセルするための cancel メソッドが用意されています。 cancel メソッドを呼び出すと、このタイマーが終了し、現在スケジュールされているすべてのタスクが破棄されます。これは、現在実行中のタスク (存在する場合) には干渉しません。タイマーが終了すると、その実行スレッドも終了し、それ以上のタスクをスケジュールできなくなります。
このタイマーによって呼び出されるタイマー タスクの run メソッド内でこのメソッドを呼び出すことで、実行中のタスクがこのタイマーによって実行された最後のタスクであることを完全に保証できることに注意してください。このメソッドは繰り返し呼び出すことができますが、2 回目以降の呼び出しは効果がありません。
別のコード例を見てみましょう:
import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TimerDemo { public static String getCurrentTime() { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(date); } public static void main(String[] args) throws InterruptedException { System.out.println("main start:"+getCurrentTime()); Timer timer = startTimer(); Thread.sleep(1000*5); //休眠5秒 System.out.println("main end:"+getCurrentTime()); timer.cancel(); } public static Timer startTimer(){ TimerTask task = new TimerTask() { @Override public void run() { System.out.println("task run:"+getCurrentTime()); } }; Timer timer = new Timer(); timer.schedule(task, 0); return timer; } }
プログラムを実行すると、出力は上記の例とまったく同じになります。違いは、main メソッドがいつ終了するかです。プロセスはアクティブに終了します。これは、タイマー スレッドが閉じられたことを意味します。
mainメソッドでcancelメソッドを呼び出したためです。 TimerTask の run メソッド内で cancel メソッドを呼び出さない場合は、実行するタスクが開始または完了していることを確認し、タスクが実行を開始していない場合は注意する必要があることに注意してください。 cancel を呼び出すだけで、すべてのタスクは実行されません。たとえば、上記のコードは、
比如上面的代码,如果我们不在main方法中调用cancel方法,而是在startTimer方法中 timer.schedule(task, 0); 语句后加上timer.cancel();语句,运行后会发现,定时器任务不会被执行,因为还未来得及执行就被取消中止了。
四、定时执行任务
上面的例子,我们介绍的是一次性任务,也就是定时器时间到了,执行完任务,后面不会再重复执行。在实际的应用中,有很多场景需要定时重复的执行同一个任务。这也分两种情况,一是每隔一段时间就执行任务,二是每天(或每周、每月等)的固定某个(或某几个)时间点来执行任务。
我们先来看第一种情况,实现每隔10秒执行同一任务的例子。代码如下:
import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TimerDemo { public static String getCurrentTime() { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(date); } public static void main(String[] args) throws InterruptedException { System.out.println("main start:"+getCurrentTime()); startTimer(); } public static void startTimer(){ TimerTask task = new TimerTask() { @Override public void run() { System.out.println("task run:"+getCurrentTime()); try { Thread.sleep(1000*3); } catch (InterruptedException e) { e.printStackTrace(); } } }; Timer timer = new Timer(); timer.schedule(task, 1000*5,1000*10); } }
执行上述程序,输出信息如下(因为定时器没有停止,重复执行任务,会不断输出,这里只拷贝了前面的一些输出)
main start:2016-01-14 08:41:14
task run:2016-01-14 08:41:19
task run:2016-01-14 08:41:29
task run:2016-01-14 08:41:39
task run:2016-01-14 08:41:49
task run:2016-01-14 08:42:00
task run:2016-01-14 08:42:10
task run:2016-01-14 08:42:20
task run:2016-01-14 08:42:30
task run:2016-01-14 08:42:40
在上面的代码中,我们调用了 timer.schedule(task, 1000*5,1000*10); 这个含义是该任务延迟5秒后执行,然后会每隔10秒重复执行。我们观察输出信息中打印的时间,是与预期一样的。 另外可以看出,间隔是以任务开始执行时间为起点算的,也就是并不是任务执行完成后再等待10秒。
Timer类有两个方法可以实现这样的功能,如下:
public void schedule(TimerTask task, long delay, long period) public void schedule(TimerTask task, Date firstTime, long period)
我们上面代码用的是第一个方法。两个方法区别在于第一次执行的时间,第一个方法是在指定延期一段时间(单位为毫秒)后执行;第二个方法是在指定的时间点执行。
这时我们考虑如下场景,如果某个任务的执行耗时超过了下次等待时间,会出现什么情况呢? 我们还是通过代码来看:
import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TimerDemo { public static String getCurrentTime() { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(date); } public static void main(String[] args) throws InterruptedException { System.out.println("main start:"+getCurrentTime()); startTimer(); } public static void startTimer(){ TimerTask task = new TimerTask() { @Override public void run() { System.out.println("task begin:"+getCurrentTime()); try { Thread.sleep(1000*10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("task end:"+getCurrentTime()); } }; Timer timer = new Timer(); timer.schedule(task, 1000*5,1000*5); } }
与前面代码相比,我们只改了2处代码和修改了下打印,一是将run方法中的sleep改为了10秒,二是将任务的执行周期改为5秒。也就说任务的执行耗时超过了任务重复执行的间隔。运行程序,前面的输出如下:
main start:2016-01-14 09:03:51
task begin:2016-01-14 09:03:56
task end:2016-01-14 09:04:06
task begin:2016-01-14 09:04:06
task end:2016-01-14 09:04:16
task begin:2016-01-14 09:04:16
task end:2016-01-14 09:04:26
task begin:2016-01-14 09:04:26
task end:2016-01-14 09:04:36
task begin:2016-01-14 09:04:36
task end:2016-01-14 09:04:46
task begin:2016-01-14 09:04:46
task end:2016-01-14 09:04:56
可以看出,每个任务执行完成后,会立即执行下一个任务。因为从任务开始执行到任务完成的耗时已经超过了任务重复的间隔时间,所以会重复执行。
五、定时执行任务(重复固定时间点执行)
我们来实现这样一个功能,每天的凌晨1点定时执行一个任务,这在很多系统中都有这种功能,比如在这个任务中完成数据备份、数据统计等耗时、耗资源较多的任务。代码如下:
import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TimerDemo { public static String getCurrentTime() { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sdf.format(date); } public static void main(String[] args) throws InterruptedException { System.out.println("main start:" + getCurrentTime()); startTimer(); } public static void startTimer() { TimerTask task = new TimerTask() { @Override public void run() { System.out.println("task begin:" + getCurrentTime()); try { Thread.sleep(1000 * 20); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("task end:" + getCurrentTime()); } }; Timer timer = new Timer(); timer.schedule(task, buildTime(), 1000 * 60 * 60 * 24); } private static Date buildTime() { Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, 1); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); Date time = calendar.getTime(); if (time.before(new Date())) { //若果当前时间已经是凌晨1点后,需要往后加1天,否则任务会立即执行。 //很多系统往往系统启动时就需要立即执行一次任务,但下面又需要每天凌晨1点执行,怎么办呢? //很简单,就在系统初始化话时单独执行一次任务(不需要用定时器,只是执行那段任务的代码) time = addDay(time, 1); } return time; } private static Date addDay(Date date, int days) { Calendar startDT = Calendar.getInstance(); startDT.setTime(date); startDT.add(Calendar.DAY_OF_MONTH, days); return startDT.getTime(); } }
因为是间隔24小时执行,没法等待观察输出。
六、小结
本文介绍了利用java Timer类如何执行定时任务的机制。可以看出,还是有许多需要注意的方法。 本文中介绍的例子,每个定时器只对应一个任务。
本文介绍的内容可以满足大部分应用场景了,但还有一些问题,比如对于一个定时器包括多个任务?定时器取消后能否再次添加任务?Timer类中还有哪些方法可用? 这些问题,我们再后面的博文中介绍。
更多Java定时任务:利用java Timer类实现定时执行任务的功能相关文章请关注PHP中文网!

javaispopularforsoss-platformdesktopapplicationsduetoits "writeonce、runaynay" philosophy.1)itusesbytecodatiTatrunnanyjvm-adipplatform.2)ライブラリリケンディンガンドジャヴァフククレアティック - ルルクリス

Javaでプラットフォーム固有のコードを作成する理由には、特定のオペレーティングシステム機能へのアクセス、特定のハードウェアとの対話、パフォーマンスの最適化が含まれます。 1)JNAまたはJNIを使用して、Windowsレジストリにアクセスします。 2)JNIを介してLinux固有のハードウェアドライバーと対話します。 3)金属を使用して、JNIを介してMacOSのゲームパフォーマンスを最適化します。それにもかかわらず、プラットフォーム固有のコードを書くことは、コードの移植性に影響を与え、複雑さを高め、パフォーマンスのオーバーヘッドとセキュリティのリスクをもたらす可能性があります。

Javaは、クラウドネイティブアプリケーション、マルチプラットフォームの展開、および言語間の相互運用性を通じて、プラットフォームの独立性をさらに強化します。 1)クラウドネイティブアプリケーションは、GraalvmとQuarkusを使用してスタートアップ速度を向上させます。 2)Javaは、埋め込みデバイス、モバイルデバイス、量子コンピューターに拡張されます。 3)Graalvmを通じて、JavaはPythonやJavaScriptなどの言語とシームレスに統合して、言語間の相互運用性を高めます。

Javaの強力なタイプ化されたシステムは、タイプの安全性、統一タイプの変換、多型を通じてプラットフォームの独立性を保証します。 1)タイプの安全性は、コンパイル時間でタイプチェックを実行して、ランタイムエラーを回避します。 2)統一された型変換ルールは、すべてのプラットフォームで一貫しています。 3)多型とインターフェイスメカニズムにより、コードはさまざまなプラットフォームで一貫して動作します。

JNIはJavaのプラットフォームの独立を破壊します。 1)JNIは特定のプラットフォームにローカルライブラリを必要とします。2)ローカルコードをターゲットプラットフォームにコンパイルおよびリンクする必要があります。3)異なるバージョンのオペレーティングシステムまたはJVMは、異なるローカルライブラリバージョンを必要とする場合があります。

新しいテクノロジーは、両方の脅威をもたらし、Javaのプラットフォームの独立性を高めます。 1)Dockerなどのクラウドコンピューティングとコンテナ化テクノロジーは、Javaのプラットフォームの独立性を強化しますが、さまざまなクラウド環境に適応するために最適化する必要があります。 2)WebAssemblyは、Graalvmを介してJavaコードをコンパイルし、プラットフォームの独立性を拡張しますが、パフォーマンスのために他の言語と競合する必要があります。

JVMの実装が異なると、プラットフォームの独立性が得られますが、パフォーマンスはわずかに異なります。 1。OracleHotspotとOpenJDKJVMは、プラットフォームの独立性で同様に機能しますが、OpenJDKは追加の構成が必要になる場合があります。 2。IBMJ9JVMは、特定のオペレーティングシステムで最適化を実行します。 3. Graalvmは複数の言語をサポートし、追加の構成が必要です。 4。AzulzingJVMには、特定のプラットフォーム調整が必要です。

プラットフォームの独立性により、開発コストが削減され、複数のオペレーティングシステムで同じコードセットを実行することで開発時間を短縮します。具体的には、次のように表示されます。1。開発時間を短縮すると、1セットのコードのみが必要です。 2。メンテナンスコストを削減し、テストプロセスを統合します。 3.展開プロセスを簡素化するための迅速な反復とチームコラボレーション。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

Dreamweaver Mac版
ビジュアル Web 開発ツール

VSCode Windows 64 ビットのダウンロード
Microsoft によって発売された無料で強力な IDE エディター

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

Safe Exam Browser
Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

ドリームウィーバー CS6
ビジュアル Web 開発ツール

ホットトピック









