تستخدم Java Callable و Future كثيرًا في برمجة المتعددة الخيوط. في الآونة الأخيرة، تعلمنا الكثير عن مواضيع جافا، لكن في بعض الأحيان نتمنى أن يمكن لموضوع أن يعيد قيمة يمكننا استخدامها. قدمت Java 5 واجهة java.util.concurrent.Callable في حزمة السير في العمليات المتزامنة والتي تشبه واجهة Runnable ولكن يمكنها إرجاع أي كائن والقدرة على رمي استثناء.
Java Callable
تستخدم واجهة Java Callable العامة لتحديد نوع العودة لكائن. توفر فئة Executors أساليب مفيدة لتنفيذ Java Callable في مجموعة خيوط. نظرًا لأن المهام القابلة للتنفيذ تعمل بشكل متواز، يجب أن ننتظر الكائن المعاد.
Java Future
تعود المهام القابلة للتنفيذ في جافا بكائن java.util.concurrent.Future. باستخدام كائن Java Future، يمكننا معرفة حالة المهمة القابلة للتنفيذ والحصول على الكائن الذي تم إرجاعه. يوفر طريقة get() التي يمكن أن تنتظر اكتمال المهمة القابلة للتنفيذ ثم ترجع النتيجة. توفر Java Future طريقة cancel() لإلغاء المهمة القابلة للتنفيذ المرتبطة. هناك نسخة متعددة الحمولة من طريقة get() حيث يمكننا تحديد الوقت الذي يجب فيه انتظار النتيجة، وهو مفيد لتجنب تعليق الموضوع الحالي لفترة أطول. توجد طرق isDone() و isCancelled() لمعرفة الحالة الحالية للمهمة القابلة للتنفيذ المرتبطة. فيما يلي مثال بسيط على مهمة قابلة للتنفيذ في جافا تُرجع اسم الموضوع الذي يقوم بتنفيذ المهمة بعد ثانية واحدة. نحن نستخدم إطار Executor لتنفيذ 100 مهمة بشكل متوازي ونستخدم Java Future للحصول على نتيجة المهام المقدمة.
package com.journaldev.threads;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MyCallable implements Callable {
@Override
public String call() throws Exception {
Thread.sleep(1000);
//إرجاع اسم الخيط الذي ينفذ هذه المهمة القابلة للفعل
return Thread.currentThread().getName();
}
public static void main(String args[]){
//احصل على ExecutorService من فئة الأدوات Executors ، حجم حوض الخيوط 10
ExecutorService executor = Executors.newFixedThreadPool(10);
//أنشئ قائمة لاحتواء كائن Future المرتبط بـ Callable
List> list = new ArrayList>();
//أنشئ مثيل MyCallable
Callable callable = new MyCallable();
for(int i=0; i< 100; i++){
//قدم مهام Callable لتنفيذها بواسطة حوض الخيوط
Future future = executor.submit(callable);
//أضف Future إلى القائمة ، يمكننا الحصول على قيمة الإرجاع باستخدام Future
list.add(future);
}
for(Future fut : list){
try {
//اطبع قيمة الإرجاع لـ Future ، لاحظ تأخير الإخراج في وحدة التحكم
// لأن Future.get() ينتظر اكتمال المهمة
System.out.println(new Date()+ "::"+fut.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
// أطفئ خدمة المنفذ الآن
executor.shutdown();
}
}
بمجرد تنفيذ البرنامج أعلاه ، ستلاحظ التأخير في الإخراج لأن طريقة Java Future get() تنتظر اكتمال مهمة Java callable. لاحظ أيضًا أنه يوجد فقط 10 خيوط ينفذون هذه المهام. إليك مقتطف من إخراج البرنامج أعلاه.
Mon Dec 31 20:40:15 PST 2012::pool-1-thread-1
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-2
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-3
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-4
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-5
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-6
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-7
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-8
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-9
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-10
Mon Dec 31 20:40:16 PST 2012::pool-1-thread-2
...
نصيحة: ماذا لو أردنا تجاوز بعض الطرق في واجهة Java Future ، على سبيل المثال تجاوز الطريقة get()
لتوقيت بعد بعض الوقت الافتراضي بدلاً من الانتظار إلى أجل غير مسمى ، في هذه الحالة تكون فئة Java FutureTask مفيدة والتي هي التنفيذ الأساسي لواجهة Future. تحقق من مثال Java FutureTask لمعرفة المزيد حول هذه الفئة.
Source:
https://www.digitalocean.com/community/tutorials/java-callable-future-example