إذا كنا بحاجة إلى تنفيذ بعض مهام التوقيت البسيطة أثناء عملية البرمجة الخاصة بنا ، فلن نحتاج إلى تحكم معقد. يمكننا التفكير في استخدام مهام توقيت المؤقت في JDK لتحقيق ذلك. تقوم LZ التالية بتحليل Timer Java Timer بناءً على مبدأها ومثالها وعيوب المؤقت.
1. مقدمة
في Java ، يجب إكمال مهمة التوقيت الكاملة بواسطة فئات Timer و TimerTask. هذه هي الطريقة التي يتم تعريفها في API. المؤقت: أداة تستخدمها لترتيب المهام التي تؤديها مؤشرات الترابط في مؤشرات ترابط الخلفية لاحقًا. يمكن تنفيذ المهام مرة واحدة ، أو يمكن تنفيذها بشكل متكرر. المهمة التي تم تحديدها بواسطة TimerTask: Timer كمهمة يتم تنفيذها أو تكرارها. يمكننا أن نفهم أن المؤقت هو أداة مؤقت تستخدم لتنفيذ المهام المحددة في موضوع خلفية ، و timertask فئة مجردة تمثل فئة الفرعية مهمة يمكن تخطيطها بواسطة المؤقت.
يوفر فئة المؤقت أربع أساليب مُنشئ في مؤقت فئة الأدوات. يبدأ كل مُنشئ مؤشر ترابط مؤقت. في الوقت نفسه ، يمكن لفئة المؤقتة التأكد من أن مؤشرات الترابط المتعددة يمكنها مشاركة كائن مؤقت واحد دون مزامنة خارجي ، وبالتالي فإن فئة الموقت آمنة مؤشرات الترابط. ومع ذلك ، نظرًا لأن كل كائن مؤقت يتوافق مع مؤشر ترابط خلفية واحد ، والذي يتم استخدامه لتنفيذ جميع مهام الموقت بالتسلسل ، بشكل عام ، يجب أن يكون الوقت الذي يقضيه في تنفيذ مهمة مؤشر ترابطنا قصيرة للغاية. ومع ذلك ، نظرًا للظروف الخاصة ، فإن وقت تنفيذ مهمة مؤقت معينة طويلة جدًا ، لذلك سوف "حصريًا" مؤشر ترابط تنفيذ المهام الموقت ، ويجب أن تنتظر جميع المواضيع اللاحقة حتى يتم تنفيذها ، مما سيؤخر تنفيذ المهام اللاحقة وجعل هذه المهام تتراكم معًا. سنقوم بتحليل الوضع المحدد لاحقًا.
عندما يقوم البرنامج بتهيئة المؤقت ، سيتم تنفيذ مهمة التوقيت وفقًا للوقت الذي حددناه. يوفر Timer طريقة الجدول الزمني ، الذي يحتوي على العديد من الأحمال الزائدة للتكيف مع المواقف المختلفة ، على النحو التالي:
الجدول الزمني (مهمة TimerTask ، وقت التاريخ): جدولة تنفيذ المهمة المحددة في الوقت المحدد.
الجدول الزمني (مهمة TimerTask ، التاريخ الأول ، الفترة الطويلة): جدولة المهمة المحددة لبدء تنفيذ التأخير الثابت المتكرر في الوقت المحدد.
الجدول الزمني (مهمة TimerTask ، تأخير طويل): يحدد المهمة المحددة التي سيتم تنفيذها بعد التأخير المحدد.
الجدول الزمني (مهمة TimerTask ، التأخير الطويل ، الفترة الطويلة): جدولة المهمة المحددة المراد تنفيذها ثابتة بشكل متكرر بعد التأخير المحدد.
في الوقت نفسه ، يتم أيضًا تحميل طريقة ScheduleAtFixedRate. طريقة ScheduleAtFixedRate هي نفس الجدول الزمني ، ولكن تركيزها مختلف ، ويتم تحليل الفرق لاحقًا.
ScheduleAtfixedRate (مهمة TimerTask ، التاريخ الأول ، الفترة الطويلة): حدد موعدًا للمهمة المحددة المراد تنفيذها بشكل متكرر بمعدل ثابت في وقت محدد.
ScheduleAtFixedRate (مهمة TimerTask ، التأخير الطويل ، الفترة الطويلة): جدولة المهمة المحددة لبدء تنفيذ معدل ثابت متكرر بعد التأخير المحدد.
Timertask
فئة TimerTask هي فئة مجردة ترتيبها المؤقت كمهمة يتم تنفيذها أو تكرارها. يحتوي على طريقة تجريدية Run () ، والتي يتم استخدامها لتنفيذ العمليات التي ستتم تنفيذها بواسطة مهمة المؤقت المقابلة. لذلك ، يجب أن ترث كل فئة مهمة محددة timertask ثم تجاوز طريقة التشغيل ().
بالإضافة إلى ذلك ، يحتوي على طريقتان غير متجانسة:
Boolean Cancel (): قم بإلغاء مهمة المؤقت هذه.
Long SchedeDexecutionTime (): إرجاع وقت التنفيذ المجدول لأحدث تنفيذ فعلي لهذه المهمة.
2. أمثلة
2.1. حدد وقت التأخير لتنفيذ مهام التوقيت
الطبقة العامة TimerTest01 {Timer Timer ؛ mimertest01 (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) ؛ }} الفئة العامة TimerTaskTest01 يمتد timertask {public void run () {system.out.println ("time's up !!!") ؛ }}نتائج التشغيل:
الطباعة الأولى:
يبدأ الموقت ....
طباعة في 3 ثوان:
الوقت يصل !!!
2.2. تنفيذ مهام التوقيت في الوقت المحدد
الطبقة العامة TimerTest02 {Timer Timer ؛ public mimertest02 () {date time = getTime () ؛ System.out.println ("حدد time time =" + time) ؛ Timer = New Timer () ؛ Timer.Schedule (New TimerTaskTest02 () ، time) ؛ } التاريخ العام getTime () {Calendar Calendar = calendar.getInstance () ؛ Calendar.set (Calendar.hour_of_day ، 11) ؛ Calendar.set (Calendar.Minute ، 39) ؛ Calendar.set (Calendar.Second ، 00) ؛ تاريخ التاريخ = calendar.getTime () ؛ وقت العودة } public static void main (string [] args) {new TimerTest02 () ؛ }} الفئة العامة TimerTaskTest02 يمتد timertask {Override public void run () {system.out.println ("تنفيذ مهام مؤشر الترابط في الوقت المحدد ...") ؛ }}عندما يصل الوقت إلى الساعة 11:39:00 ، سيتم تنفيذ مهمة الخيط ، بالطبع ، سيتم تنفيذها حتى لو كانت أكبر من ذلك الوقت! ! نتيجة التنفيذ هي:
الوقت المحدد الوقت = الثلاثاء 10 يونيو 11:39:00 CST 2014 الوقت المحدد لتنفيذ مهام سلسلة الرسائل ...
2.3. بعد تأخير الوقت المحدد ، سيتم تنفيذ مهمة التوقيت في وقت الفاصل الزمني المحدد.
الطبقة العامة TimerTest03 {Timer Timer ؛ public mimertest03 () {timer = new timer () ؛ Timer.Schedule (New TimerTaskTest03 () ، 1000 ، 2000) ؛ } public static void main (string [] args) {new TimerTest03 () ؛ }} الفئة العامة timertasktest03 يمتد timertask {Override public void run () {date date = new date (this.scheduledExecutionTime ()) ؛ System.out.println ("وقت تنفيذ هذا الموضوع هو:" + تاريخ) ؛ }}نتائج التشغيل:
إن وقت تنفيذ هذا الموضوع هو: Tue 10 Jun 10 21:19:47 CST 2014 الوقت لتنفيذ هذا الموضوع هو: Tue 10 Jun 10 21:19:49 CST 2014 Time Time لتنفيذ هذا الموضوع هو: Tue 10 يونيو 21:19:51 2014 وقت تنفيذ هذا الموضوع هو: Tue Jun 10 21:19:57 CST 2014 ...............
بالنسبة لمهمة الموضوع هذه ، إذا لم نتوقف عن المهمة ، فسيستمر تشغيلها.
بالنسبة للأمثلة الثلاثة المذكورة أعلاه ، أظهرت LZ ذلك لفترة وجيزة ، ولم تشرح مثال طريقة ScheduleAtFixedRate. في الواقع ، هذه الطريقة هي نفس طريقة الجدول!
2.4. تحليل الجدول الزمني و ScheduleAtfixedrate
(1) الجدول (مهمة TimerTask ، وقت التاريخ) ، الجدول الزمني (مهمة TimerTask ، تأخير طويل)
بالنسبة لكلتا الطريقتين ، إذا كان SchedeDexecutionTime المحدد <= SystemCurrentTime ، فسيتم تنفيذ المهمة على الفور. لن يتغير SignedExecutionTime بسبب التنفيذ المفرط للمهمة.
(2) الجدول الزمني (مهمة TimerTask ، التاريخ الأول ، الفترة الطويلة) ، الجدول الزمني (Timertask Task ، تأخير طويل ، فترة طويلة)
هاتان الطريقتان مختلفتان بعض الشيء عن اثنين أعلاه. كما ذكرنا سابقًا ، سيتم تأخير مهمة مؤقت المؤقت لأن المهمة السابقة يتم تنفيذها لفترة طويلة. في هاتين الطريقتين ، سيتغير الوقت المحدد لكل مهمة تم تنفيذه مع الوقت الفعلي للمهمة السابقة ، أي أن enderedExecutionTime (n+1) = relexecutiontime (n)+time. وهذا يعني ، إذا تسببت المهمة التاسعة في عملية وقت التنفيذ هذه بسبب بعض المواقف ، وأخيراً SystemCurrentTime> = SchedeDexecutionTime (n+1) ، فهذه هي المهمة N+1 ولن يتم تنفيذها بسبب الوقت. ستنتظر تنفيذ المهمة التاسعة قبل التنفيذ ، ثم سيؤدي ذلك حتماً إلى إصدار تنفيذ N+2 SchedeDexecutionTime وتغييره ، أي أن الوقت ScheduledExecutionTime (N+2) = فترة التأمل (N+1)+فترة. لذلك ، تولي هاتان الطريقتان المزيد من الاهتمام لاستقرار وقت التخزين.
(3) SCHEDULEATFIXEDRATE (مهمة TIMERTASK ، التاريخ الأول ، الفترة الطويلة) ، SCHEDULEATFIXEDRATE (مهمة TIMERTASK ، تأخير طويل ، فترة طويلة)
كما ذكرنا سابقًا ، فإن تركيز طرق ScheduleAtfixedrate وجدوله مختلف. تركز طريقة الجدول الزمني على استقرار وقت الادخار ، بينما تركز طريقة ScheduleAtFixedRate بشكل أكبر على الحفاظ على استقرار تردد التنفيذ. لماذا تقول ذلك ، الأسباب هي كما يلي. في طريقة الجدول الزمني ، سوف يتسبب تأخير المهمة السابقة في تأخير مهمة التوقيت بعد ذلك ، في حين أن طريقة ScheduleAtFixedRate لن تفعل ذلك. إذا كان وقت التنفيذ للمهمة التاسعة طويلة جدًا ، فإن SystemCurrentTime> = SchededExecutionTime (N+1) ، فلن يكون هناك انتظار لتنفيذ مهمة N+1 على الفور. لذلك ، تختلف طريقة الحساب لوقت تنفيذ طريقة ScheduleAtFixedRate عن الجدول الزمني ، ولكن ScheduleDexecutionTime (n) = FirstExecuteTime +N*فترة ، وستظل طريقة الحساب لم تتغير إلى الأبد. لذلك يركز ScheduleAtFixedRate أكثر على الحفاظ على تردد التنفيذ مستقر.
3. عيوب الموقت
3.1. عيوب المؤقت
يمكن لوقت المؤقت الوقت (تنفيذ المهام في الوقت المحدد) ، والتأخير (مهام التأخير في 5 ثوان) ، وتنفيذ المهام بشكل دوري (تنفيذ المهام في ثانية واحدة) ، ولكن الموقت لديه بعض أوجه القصور. بادئ ذي بدء ، يعتمد دعم المؤقت للجدولة على الوقت المطلق ، وليس الوقت النسبي ، لذلك فهو حساس للغاية للتغيرات في وقت النظام. ثانياً ، لن يلتقط موضوع المؤقت استثناءات. إذا تم إلقاء الاستثناء غير المحدد بواسطة TimerTask ، فسوف يتسبب ذلك في إنهاء مؤشر ترابط المؤقت. في الوقت نفسه ، لن يستأنف المؤقت تنفيذ مؤشر الترابط ، وسيؤمن عن طريق الخطأ أنه سيتم إلغاء مؤشر ترابط المؤقت بالكامل. في الوقت نفسه ، لن يتم تنفيذ TimerTask ، التي تم تحديد موعد لمقدمها بعد ، ولا يمكن جدولة مهام جديدة. لذلك ، إذا قام Timertask بإلقاء استثناء غير محدد ، فإن المؤقت سوف ينتج سلوكًا لا يمكن التنبؤ به.
(1) عيوب تأخير وقت إدارة الموقت. قبل أن يقوم الموقت بإنشاء مهمة مؤشر ترابط واحدة فقط عند تنفيذ مهمة توقيت. إذا كان هناك عدة مؤشرات ترابط ، إذا تسبب إحدى مؤشرات الترابط في أن يكون وقت تنفيذ مهمة مؤشر الترابط طويلة جدًا لسبب ما ، وسيحدث الفاصل بين المهمتين ، ستحدث بعض العيوب:
الطبقة العامة timertest04 {private timer timer ؛ بداية طويلة عامة ؛ public mimertest04 () {this.timer = new timer () ؛ start = system.currentTimeMillis () ؛ } public void timerOne () {timer.schedule (new timertask () {public void run () {system.out.println ("timerone ادعاء ، الوقت: }}}} ، 1000) ؛ } public void timertwo () {timer.schedule (new timertask () {public void run () {system.out.println ( } public static void main (string [] args) يلقي الاستثناء {timertest04 test = new timertest04 () ؛ test.timerone () ؛ test.timertwo () ؛ }}وفقًا لتفكيرنا الطبيعي ، يجب تنفيذ Timertwo بعد 3s ، ويجب أن تكون النتيجة:
تم استدعاء Timerone ، الوقت: 1001 تم استدعاء Timerone ، الوقت: 3001
لكن الأمور تتعارض مع توقعاتي. Timerone Sleeps (4000) ، ينام 4s ، والوقت داخل المؤقت ، والذي يسبب الوقت اللازم لـ TimeOne لتجاوز الفاصل الزمني. النتيجة:
تم استدعاء Timerone ، الوقت: 1000 تم استدعاء Timerone ، الوقت: 5000
(2) توقيت يلقي عيب استثناء. إذا قام TimerTask بإلقاء RuntimeException ، فسيقوم المؤقت بإنهاء تشغيل جميع المهام. على النحو التالي:
الطبقة العامة timertest04 {private timer timer ؛ public mimertest04 () {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.
استثناء في الموضوع "Timer-0" java.lang.runtimeexception في com.chenssy.timer.timertest04 $ 1.run (timertest04.java:25) على java.util.timerthread.mainloop (timer.java:555) على java.util.timerthread.run.
بالنسبة لعيوب الموقت ، يمكننا النظر في SecrettHreadPoolexecutor كبديل. يعتمد المؤقت على الوقت المطلق وهو أكثر حساسية لوقت النظام ، في حين أن ScredulThreadPoolexecutor يعتمد على الوقت النسبي ؛ Timer عبارة عن مؤشر ترابط واحد داخليًا ، في حين أن ScurdeThreadPoolexecutor هو تجمع مؤشرات ترابط داخليًا ، بحيث يمكنه دعم التنفيذ المتزامن لمهام متعددة.
3.2. استبدال المؤقت مع SchedeDexecutorService
(1) حل المشكلة الأولى:
فئة عامة Schedulexecutortest {private ScredeDexecutorService Scheduexec ؛ بداية طويلة عامة ؛ ScheduleDexecutortest () {this.scheduexec = evelopors.newsCheduleDthReadPool (2) ؛ this.start = system.currentTimeMillis () ؛ } public void timerOne () {scheduexec.schedule (new RunNable () {public void run () {system.out.println ("timeerOne ، time:" + (system.currentTimeMillis () - start)) ؛ try {thread.sleep (4000) ؛ } ، 1000 ، timeunit.milliseconds) ؛ } public void timertwo () {ScheduleExec.Schedule (new RunNable () {public void run () {system.out.println ("timertwo ، time:" + (system.currenttimeMillis () - start)) ؛}} ، 2000 ، timeunit.milliseconds) ؛ } public static void main (string [] args) {ScrededExecutortest test = new ScheduleDexecutortest () ؛ test.timerone () ؛ test.timertwo () ؛ }}نتائج التشغيل:
Timerone ، الوقت: 1003 Timertwo ، الوقت: 2005
(2) حل المشكلة اثنين
فئة عامة Schedulexecutortest {private ScredeDexecutorService Scheduexec ؛ بداية طويلة عامة ؛ ScheduleDexecutortest () {this.scheduexec = evelopors.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 () {ScheduleExec.ScheDuleAtfixedRate (new RunNable () {public void run () {system.out.println ("Timertwo invoked .....") ؛}} ، 2000،500 ، timunit.milliseconds) ؛ } public static void main (string [] args) {ScrededExecutortest test = new ScheduleDexecutortest () ؛ test.timerone () ؛ test.timertwo () ؛ }}نتائج التشغيل:
تم استدعاء Timertwo ... استدعى Timertwo ... Timertwo استدعى ... Timertwo استدعى ... Timertwo استدعى ... Timertwo استدعى ... Timertwo استدعاء ...
4. استخدم المؤقت لتحقيق الكرة
مثال في كتاب المحاكاة ، قام بالكرة الدبقية ، والتي كانت ترسم دوائر متعددة في الوضع المحدد على القماش ، وبعد فترة من التأخير ، تم إعادة رسمها في وضع قريب. اجعل الكرة تتحرك ، وضبط التأخير من خلال مكون Jspinner للتحكم في سرعة حركة الكرة.
ballscanvas.java
يمتد Ballscanvas من الطبقة العامة Canvas Canvas ActionListener ، FocusListener {Private Ball Balls [] ؛ // كرات متعددة مؤقت مؤقت ؛ كرة فئة ثابتة خاصة {int x ، y ؛ // تنسيق لون اللون ؛ // اللون المنطقي ، اليسار ؛ // اتجاه كرة الحركة (int x ، int y ، لون اللون) {this.x = x ؛ this.y = y ؛ this.color = اللون ؛ Up = Left = false ؛ }} pallscanvas العامة (ألوان الألوان [] ، تأخير int) {// تهيئة اللون وتأخير this.balls = new ball [color.length] ؛ لـ (int i = 0 ، x = 40 ؛ i <color.length ؛ i ++ ، x+= 40) {balls [i] = new ball (x ، x ، colors [i]) ؛ } this.addfocusListener (this) ؛ توقيت = توقيت جديد (تأخير ، هذا) ؛ // إنشاء كائن مؤقت ، حدد تأخير Timer.start () ؛ }. } // ارسم الطلاء الباطل العام (الرسومات g) {for (int i = 0 ؛ i <balls.length ؛ i ++) {g.setColor (balls [i] .Color) ؛ // تعيين كرات الألوان [i] .x = الكرات [i] .LEFT؟ الكرات [i] .x - 10: كرات [i] .x + 10 ؛ if (الكرات [i] .x <0 || الكرات [i] .x> = this.getWidth ()) {// تغيير الاتجاه إلى الكرات الأفقية [i] .left =! الكرات [i] .LEFT ؛ } الكرات [i] .y = الكرات [i] .up؟ الكرات [i] .y - 10: الكرات [i] .y + 10 ؛ إذا (الكرات [i] } g.filloval (الكرات [i] .x ، الكرات [i] .y ، 20 ، 20) ؛ // ارسم دائرة من القطر المحدد}} // تنفيذ التوقيت المحدد حدث Override public void actionperformed (ActionEvent e) {REPAINT () ؛ // REPAINT} // GET FOCUD Override public void focusgained (Focusevent E) {timer.stop () ؛ // Timer Stop} // Lost Focus Override public void FocusLost (Focusevent E) {timer.restart () ؛ // Timer Restart}}ballsjframe.java
Class BallsjFrame يمتد JFrame ينفذ changelistener {private ballscanvas ball ؛ خاص Jspinner Spinner ؛ ballsjframe () {super ("pinball") ؛ this.setBounds (300 ، 200 ، 480 ، 360) ؛ this.setDefaultCloseOperation (exit_on_close) ؛ ألوان الألوان [] = {color.red ، color.green ، color.blue ، color.magenta ، color.cyan} ؛ الكرة = الكرة الجديدة (الألوان ، 100) ؛ this.getContentPane (). add (ball) ؛ JPanel Panel = New JPanel () ؛ this.getContentPane (). add (لوحة ، "الجنوب") ؛ Pane.add (New Jlabel ("Delay")) ؛ spinner = new jspinner () ؛ spinner.setValue (100) ؛ Pane.add (Spinner) ؛ spinner.addchangelistener (هذا) ؛ this.setVisible (صحيح) ؛ } Override public void statechanged (changeEvent e) {// عند تعديل قيمة jspinner ، انقر فوق الزر لأعلى أو لأسفل لـ jspinner ، أو اضغط على Enter in jspinner ball.setdelay (integer.parseint ("" + spinner.getValue ())) ؛ } public static void main (string [] args) {new ballsjframe () ؛ }} الآثار هي كما يلي: