0Pricing
Java Academy · درس

‏ScheduledExecutorService للمهام المتكررة

جدوِل المهام بمعدلات ثابتة أو بتأخيرات ثابتة لتنفيذ الاستطلاع وأعمال الصيانة في الخلفية

‏ScheduledExecutorService للمهام المتكررة درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.

لماذا نستخدم ScheduledExecutorService؟

يوسّع ScheduledExecutorService واجهة ExecutorService لدعم جدولة المهام. وهو يستبدل الفئتين القديمتين Timer/TimerTask بمجدول مدعوم بمجموعة خيوط وآمن تجاه الاستثناءات.

import java.util.concurrent.*;

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);

// One-shot delay
scheduler.schedule(
    () -> System.out.println("Runs once after 3 seconds"),
    3, TimeUnit.SECONDS
);

// Don't forget to shut down eventually:
// scheduler.shutdown();

schedule: تأخير لمرة واحدة

ينفّذ schedule(task, delay, unit) المهمة مرة واحدة بعد التأخير المحدد. ويعمل مع كل من Runnable وCallable:

ScheduledExecutorService s = Executors.newScheduledThreadPool(1);

// Runnable:
ScheduledFuture<?> sf1 = s.schedule(
    () -> System.out.println("Runnable fired!"), 2, TimeUnit.SECONDS);

// Callable:
ScheduledFuture<Integer> sf2 = s.schedule(
    () -> { System.out.println("Callable fired!"); return 42; },
    2, TimeUnit.SECONDS);

System.out.println(sf2.get()); // 42 (after 2 seconds)

scheduleAtFixedRate

ينفّذ scheduleAtFixedRate(task, initialDelay, period, unit) المهمة بصورة متكررة بمعدل ثابت. وإذا استغرقت المهمة وقتًا أطول من الفترة، يبدأ التنفيذ التالي فور انتهائها، من دون تداخل.

ScheduledExecutorService s = Executors.newScheduledThreadPool(1);
AtomicInteger count = new AtomicInteger();

ScheduledFuture<?> sf = s.scheduleAtFixedRate(
    () -> System.out.println("Tick " + count.incrementAndGet()),
    0,   // initial delay
    1,   // period
    TimeUnit.SECONDS
);

Thread.sleep(5000);
sf.cancel(false); // stop after 5 ticks
s.shutdown();

scheduleWithFixedDelay

ينتظر scheduleWithFixedDelay(task, initialDelay, delay, unit) التأخير المحدد بعد اكتمال كل مهمة، ثم يبدأ المهمة التالية. ويُعد مناسبًا للاستطلاع عندما تختلف مدة المهمة.

s.scheduleWithFixedDelay(
    () -> {
        System.out.println("Polling... " + Instant.now());
        // variable-length polling task
    },
    0,  // no initial delay
    5,  // 5-second gap AFTER each completion
    TimeUnit.SECONDS
);
// Next execution = task end + 5 seconds (not task start)

المعدل الثابت مقابل التأخير الثابت

التمييز الأساسي:

  • scheduleAtFixedRate: بدء التنفيذ التالي = بدء التنفيذ السابق + الفترة (توقيت بلا انجراف)
  • scheduleWithFixedDelay: بدء التنفيذ التالي = انتهاء التنفيذ السابق + التأخير (فاصل مضمون بين عمليات التنفيذ)

استخدم fixedRate لإشارات البقاء، وfixedDelay للاستطلاع عندما تختلف مدة المهمة.

ScheduledFuture: الإلغاء

تعيد schedule/scheduleAtFixedRate/scheduleWithFixedDelay كائنًا من النوع ScheduledFuture. استدعِ cancel(false) لإيقاف عمليات التنفيذ المستقبلية من دون مقاطعة العملية الجارية:

ScheduledFuture<?> sf = scheduler.scheduleAtFixedRate(
    () -> System.out.println("Heartbeat"), 0, 1, TimeUnit.SECONDS);

// Cancel after 5 seconds
scheduler.schedule(() -> sf.cancel(false), 5, TimeUnit.SECONDS);

معالجة الاستثناءات في المهام المجدولة

إذا طرحت مهمة مجدولة استثناءً غير معالج، فإن المجدول يتوقف بصمت عن التنفيذ المتكرر، ولن تُنفَّذ المهمة مرة أخرى. غلّف المهمة دائمًا داخل try-catch:

scheduler.scheduleAtFixedRate(() -> {
    try {
        // risky work
        if (Math.random() < 0.3) throw new RuntimeException("transient");
        System.out.println("Success");
    } catch (Exception e) {
        System.err.println("Task error: " + e.getMessage());
        // task continues to run on next interval
    }
}, 0, 2, TimeUnit.SECONDS);

تطبيق عملي: تحديث ذاكرة التخزين المؤقت

استخدم مهمة مجدولة لتحديث ذاكرة التخزين المؤقت دوريًا:

class PriceCache {
    private volatile Map<String, Double> prices = new HashMap<>();
    
    PriceCache(ScheduledExecutorService scheduler) {
        scheduler.scheduleAtFixedRate(this::refresh, 0, 30, TimeUnit.SECONDS);
    }
    
    private void refresh() {
        System.out.println("Refreshing prices...");
        // prices = fetchFromDatabase();
    }
    
    Double getPrice(String symbol) { return prices.get(symbol); }
}

تطبيق عملي: إعادة المحاولة مع التراجع التدريجي

نفّذ إعادة المحاولة بالتراجع الأُسّي باستخدام المهام المجدولة:

void retryWithBackoff(Runnable task, int maxRetries, ScheduledExecutorService s) {
    AtomicInteger attempts = new AtomicInteger(0);
    long[] delay = {1}; // seconds
    
    Runnable wrapper = new Runnable() {
        public void run() {
            try {
                task.run();
            } catch (Exception e) {
                int n = attempts.incrementAndGet();
                if (n < maxRetries) {
                    delay[0] *= 2;
                    s.schedule(this, delay[0], TimeUnit.SECONDS);
                } else System.err.println("Max retries reached");
            }
        }
    };
    s.execute(wrapper);
}

المراقبة: getDelay وgetQueue

افحص حالة المجدول:

ScheduledFuture<?> sf = scheduler.schedule(() -> {}, 10, TimeUnit.SECONDS);
System.out.println("Delay remaining: " + sf.getDelay(TimeUnit.SECONDS) + "s");

// ThreadPoolExecutor provides queue inspection:
if (scheduler instanceof ScheduledThreadPoolExecutor ste) {
    System.out.println("Queue size: " + ste.getQueue().size());
}

إيقاف التشغيل وتصريف المهام

عند إيقاف التشغيل، حدّد ما إذا كان ينبغي أن تستمر المهام المجدولة في التنفيذ:

ScheduledThreadPoolExecutor ste = (ScheduledThreadPoolExecutor)
    Executors.newScheduledThreadPool(2);

// Cancel scheduled tasks on shutdown (default: false = they run)
ste.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
ste.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);

ste.shutdown();

اختبار سريع

تستغرق مهمة مجدولة باستخدام scheduleAtFixedRate(..., 1, TimeUnit.SECONDS) ثلاث ثوانٍ حتى تكتمل. كيف يتعامل المجدول مع ذلك؟

مراجعة: ScheduledExecutorService

أهم النقاط:

  • schedule() — تنفيذ مرة واحدة بعد تأخير
  • scheduleAtFixedRate() — توقيت بلا انجراف (من بداية إلى بداية)
  • scheduleWithFixedDelay() — فجوة مضمونة (من نهاية إلى بداية)
  • تؤدي الاستثناءات غير الملتقطة إلى إيقاف المهام المتكررة بصمت — غلّفها دائمًا داخل try-catch
  • ألغِ المهمة باستخدام ScheduledFuture.cancel(false)

الأسئلة الشائعة

هل درس «‏ScheduledExecutorService للمهام المتكررة» مجاني؟

نعم — نص درس «‏ScheduledExecutorService للمهام المتكررة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.

ماذا ستتعلم في «‏ScheduledExecutorService للمهام المتكررة»؟

جدوِل المهام بمعدلات ثابتة أو بتأخيرات ثابتة لتنفيذ الاستطلاع وأعمال الصيانة في الخلفية تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟

لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «‏ScheduledExecutorService للمهام المتكررة»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟

نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. ExecutorService وأنواع تجمعات الخيوط
  2. إرسال المهام: Runnable مقابل Callable
  3. Future ومعالجة الأخطاء
  4. ‏ScheduledExecutorService للمهام المتكررة
← العودة إلى Java Academy