0Pricing
Java Academy · درس

Future ومعالجة الأخطاء

استخدم Future.get مع مهلات زمنية، وعالج ExecutionException، وألغِ المهام قيد التشغيل

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

ما هو Future؟

يمثل Future<V> النتيجة المعلّقة لعملية حسابية غير متزامنة. ويوفر أساليب للتحقق من الاكتمال والانتظار للحصول على النتيجة ومعالجة الأخطاء.

import java.util.concurrent.*;

ExecutorService pool = Executors.newFixedThreadPool(2);
Future<Integer> future = pool.submit(() -> {
    Thread.sleep(1000);
    return 42;
});

System.out.println("Is done? " + future.isDone()); // false
Integer result = future.get(); // blocks
System.out.println("Result: " + result); // 42
pool.shutdown();

future.get() مع مهلة زمنية

فضّل دائمًا الصيغة التي تتضمن مهلة زمنية لمنع التعليق إلى أجل غير محدد:

try {
    Integer result = future.get(5, TimeUnit.SECONDS);
    System.out.println(result);
} catch (TimeoutException e) {
    System.out.println("Timed out");
    future.cancel(true); // interrupt the task
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
} catch (ExecutionException e) {
    System.out.println("Task threw: " + e.getCause());
}

ExecutionException

إذا طرحت المهمة استثناءً، فإن future.get() يغلّفه داخل ExecutionException. فكّ التغليف باستخدام getCause():

Future<String> failing = pool.submit(() -> {
    throw new IllegalArgumentException("bad input");
});

try {
    failing.get();
} catch (ExecutionException e) {
    Throwable cause = e.getCause();
    System.out.println(cause.getClass().getSimpleName()); // IllegalArgumentException
    System.out.println(cause.getMessage()); // bad input
}

إلغاء Future

يحاول cancel(mayInterruptIfRunning) إلغاء المهمة. ويعيد القيمة true إذا نجح. وإذا كانت قيمة mayInterruptIfRunning هي true، تُرسل إشارة مقاطعة إلى خيط التنفيذ قيد التشغيل.

Future<String> f = pool.submit(() -> {
    try {
        Thread.sleep(10_000); // long task
        return "done";
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        return "interrupted";
    }
});

boolean cancelled = f.cancel(true); // send interrupt
System.out.println("Cancelled: " + cancelled); // true
System.out.println("Is cancelled: " + f.isCancelled()); // true

isCancelled مقابل isDone

يعيد isDone() القيمة true إذا اكتملت المهمة بصورة طبيعية، أو طرحت استثناءً، أو أُلغيت. ولا يعيد isCancelled() القيمة true إلا إذا أُلغيت المهمة.

// After cancel:
System.out.println(f.isDone());       // true
System.out.println(f.isCancelled()); // true

// After normal completion:
Future<Integer> done = pool.submit(() -> 5);
done.get(); // wait
System.out.println(done.isDone());       // true
System.out.println(done.isCancelled()); // false

CompletionService للنتائج المتاحة أولًا

يغلّف ExecutorCompletionService مجموعة خيوط ويوفر أسلوب take() لاسترداد Future المكتمل التالي، وفق ترتيب الاكتمال لا ترتيب الإرسال:

ExecutorCompletionService<Integer> cs =
    new ExecutorCompletionService<>(pool);

for (int i = 0; i < 5; i++) {
    final int delay = 5 - i; // submit fastest last
    cs.submit(() -> { Thread.sleep(delay * 100); return delay; });
}

for (int i = 0; i < 5; i++) {
    Future<Integer> f = cs.take(); // next completed
    System.out.println(f.get());
}

معالجة Futures متعددة بأمان

عند جمع النتائج من Futures متعددة، كرّر عليها وعالج الاستثناءات لكل Future على حدة:

List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 5; i++) {
    final int id = i;
    futures.add(pool.submit(() ->
        id % 2 == 0 ? "ok_" + id : (() -> { throw new RuntimeException("fail_"+id); }).get()
    ));
}

for (Future<String> f : futures) {
    try {
        System.out.println(f.get());
    } catch (ExecutionException e) {
        System.out.println("Error: " + e.getCause().getMessage());
    }
}

Future.get() بعد إيقاف المجموعة

يؤدي الإرسال بعد shutdown() إلى طرح RejectedExecutionException. لكن يمكن استرداد Futures التي أُرسلت قبل الإيقاف حتى بعد إيقاف المجموعة:

ExecutorService pool2 = Executors.newFixedThreadPool(2);
Future<Integer> f = pool2.submit(() -> 99);
pool2.shutdown(); // no new tasks

// Still valid — task was submitted before shutdown:
System.out.println(f.get()); // 99

try {
    pool2.submit(() -> 0); // throws!
} catch (RejectedExecutionException e) {
    System.out.println("Pool shut down");
}

FutureTask: Future يدوي

يطبّق FutureTask<V> كلًا من Runnable وFuture<V> — وهو مفيد عندما تحتاج إلى Future من دون ExecutorService:

FutureTask<String> task = new FutureTask<>(() -> "computed");
new Thread(task).start(); // run in any thread
System.out.println(task.get()); // computed

نمط إعادة المحاولة باستخدام Future

نفّذ منطقًا بسيطًا لإعادة المحاولة حول future.get():

Future<String> future = pool.submit(() -> {
    if (Math.random() < 0.7) throw new RuntimeException("transient error");
    return "success";
});

for (int attempt = 0; attempt < 3; attempt++) {
    try {
        System.out.println(future.get());
        break;
    } catch (ExecutionException e) {
        System.out.println("Attempt " + (attempt+1) + " failed");
        if (attempt == 2) throw e;
        future = pool.submit(() -> "retry"); // resubmit
    }
}

قيود Future

لدى java.util.concurrent.Future بعض القيود:

  • لا يدعم دوال الاستدعاء اللاحق — يجب الحجب باستخدام get()
  • لا يمكن تسلسل التحويلات
  • لا يمكن دمج Futures متعددة

استخدم CompletableFuture (في الدورة التالية) للتركيب التفاعلي غير الحاجب.

اختبار سريع

تطرح مهمة استثناءً. ما الاستثناء الذي يُطرح عند استدعاء future.get()؟

مراجعة: Future ومعالجة الأخطاء

أهم النقاط:

  • يحجب future.get() التنفيذ؛ استخدم دائمًا الصيغة التي تتضمن مهلة زمنية
  • يغلف ExecutionException استثناءات المهمة؛ فكّ التغليف باستخدام getCause()
  • يرسل cancel(true) إشارة مقاطعة؛ استخدم isDone/isCancelled للتحقق من الحالة
  • يسترد ExecutorCompletionService.take() Futures وفق ترتيب اكتمالها
  • يطبّق FutureTask كلًا من Runnable وFuture

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

هل درس «Future ومعالجة الأخطاء» مجاني؟

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

ماذا ستتعلم في «Future ومعالجة الأخطاء»؟

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

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

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

كم من الوقت يستغرق درس «Future ومعالجة الأخطاء»؟

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

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

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

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

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