كيفية الاستيلاء على استثناءات في مستقبلات
-
30-09-2019 - |
سؤال
بعد العثور على ذلك FutureTask
الجري في Executors.newCachedThreadPool()
على Java 1.6 (ومن Eclipse) ابتلاع الاستثناءات في Runnable.run()
الطريقة ، لقد حاولت التوصل إلى طريقة للقبض عليها دون إضافة رمي/التقاط كل ما عندي Runnable
التطبيقات.
تشير واجهة برمجة التطبيقات إلى أن التجاوز FutureTask.setException()
يجب أن تساعد في هذا:
يسبب هذا المستقبل الإبلاغ عن ExecutionException مع قابلية الرمي المحددة كسبب ، ما لم يتم بالفعل تعيين هذا المستقبل أو تم إلغاؤه. يتم استدعاء هذه الطريقة داخليًا بواسطة طريقة التشغيل عند فشل الحساب.
ومع ذلك ، لا يبدو أن هذه الطريقة تسمى (يظهر تشغيل مصحح الأخطاء أن الاستثناء يتم اكتشافه بواسطة FutureTask
, ، لكن setException
لا يسمى). لقد كتبت البرنامج التالي لإعادة إنتاج مشكلتي:
public class RunTest {
public static void main(String[] args) {
MyFutureTask t = new MyFutureTask(new Runnable() {
@Override
public void run() {
throw new RuntimeException("Unchecked exception");
}
});
ExecutorService service = Executors.newCachedThreadPool();
service.submit(t);
}
}
public class MyFutureTask extends FutureTask<Object> {
public MyFutureTask(Runnable r) {
super(r, null);
}
@Override
protected void setException(Throwable t) {
super.setException(t);
System.out.println("Exception: " + t);
}
}
سؤالي الرئيسي هو: كيف يمكنني التقاط الاستثناءات التي تم إلقاؤها في مستقبلات مستقبلية؟ لماذا لا setException
الحصول على الاتصال؟
أيضا أود أن أعرف لماذا Thread.UncaughtExceptionHandler
لا تستخدم الآلية بواسطة FutureTask
, ، هل هناك أي سبب لهذا؟
المحلول
setException
ربما لم يتم إجراؤها من أجل تجاوزها ، ولكن يتم توفيرها للسماح لك بتعيين النتيجة إلى استثناء ، في حالة حاجة. ما تريد القيام به هو تجاوز done()
الطريقة وحاول الحصول على النتيجة:
public class MyFutureTask extends FutureTask<Object> {
public MyFutureTask(Runnable r) {
super(r, null);
}
@Override
protected void done() {
try {
if (!isCancelled()) get();
} catch (ExecutionException e) {
// Exception occurred, deal with it
System.out.println("Exception: " + e.getCause());
} catch (InterruptedException e) {
// Shouldn't happen, we're invoked when computation is finished
throw new AssertionError(e);
}
}
}
نصائح أخرى
هل حاولت استخدام UncaughtExceptionHandler
?
- تحتاج إلى تنفيذ
UncaughtExceptionHandler
واجهه المستخدم. - لتعيين
UncaughtExceptionHandler
لخيوط البلياردو ، قدم أThreadFactory
في الExecutor.newCachedThreadPool(ThreadFactory)
مكالمة. - يمكنك تعيين uncaughtexceptionHandler للمعلومات التي تم إنشاؤها عبر
setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
إرسال المهام مع ExecutorService.execute
, ، لأن الاستثناءات فقط التي تم طرحها من المهام المقدمة مع execute
اجعلها إلى معالج الاستثناء غير الموقوف. للمهام المقدمة مع ExecutorService.submit
يعتبر أي استثناء تم إلقاؤه جزءًا من قيمة إرجاع المهمة. إذا انتهت المهمة التي تم تقديمها مع إرسال باستثناء ، فستكون إعادة تدويرها عند الاتصال Future.get
, ، ملفوفة في ExecutionException
حل أفضل بكثير:فحص إكمال جافا فوتوريتاس
عندما تتصل futureTask.get()
لاسترداد نتيجة الحساب ، سوف يلقي استثناء (ExecutionException) إذا كان الأساس Runnable
/Callable
ألقى استثناء.
ExecutionException.getCause()
سيعود إلى استثناء أن ال Runnable
/Callable
يرمي.
سوف يلقي أيضا استثناء مختلف إذا كان Runnable
/Callable
تم الالغاء.
لقد نظرت إلى الكود المصدري لـ FutureTask
ولم يتمكن من العثور على أين setException
يتم استدعاؤه.
هناك innerSetException
طريقة من FutureTask.Sync
(فئة داخلية من FutureTask
) التي يتم استدعاؤها في حالة Throwable
يتم إلقاؤها بواسطة طريقة التشغيل. يتم استدعاء هذه الطريقة أيضًا setException
.
لذلك فإن طبقات مثل Javadoc غير صحيحة (أو من الصعب جدًا فهمها ...).
هناك ثلاث طرق قياسية وطريقة واحدة مرتجلة. 1. استخدم uncaughtexceptionHandler ، وضبط uncaughtexceptionHandler
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable ex) {..}}
*لكن القيد هو أنه يمسك بالاستثناء الذي ألقاه الخيط ولكن في حالة المهمة المستقبلية ، يتم ابتلاعه. 2. استخدام afterExecute
بعد إنشاء ThreadPoolexecutor مخصص مع الخطاف الذي تم توفيره خصيصا لهذا الغرض. النظر في مدونة ThreadPoolexecutor ، عبر إرسال> تنفيذ (هناك workqueue ، workQueue.offer
) ، تتم إضافة المهام إلى قائمة انتظار العمل
final void runWorker(Worker arg0) {
Thread arg1 = Thread.currentThread();
Runnable arg2 = arg0.firstTask;
..
while(arg2 != null || (arg2 = this.**getTask()**) != null) {
arg0.lock();
..
try {
this.beforeExecute(arg1, arg2);
Object arg4 = null;
try {
arg2.run();
} catch (RuntimeException arg27) {
..
} finally {
this.**afterExecute**(arg2, (Throwable)arg4);
}
}
getTask() {..
this.workQueue.**poll**();
..}
ثم ، والثالث يستخدم Try Try Catch داخل طريقة الاتصال ولكن لا يمكنك التقاط الاستثناء في الخارج هنا.
يقوم الحل البديل باستدعاء جميع أساليب الاتصال من طريقة استدعاء لـ TaskFactory ، وهو مصنع يطلق Callables.