كيفية التقاط بشكل صحيح RuntimeExceptions من المنفصين؟
-
18-09-2019 - |
سؤال
قل أن لدي التعليمات البرمجية التالية:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);
الآن، إذا myRunnable
يلقي أ RuntimeExcpetion
, ، كيف يمكنني التقاطه؟ طريقة واحدة ستكون لتزويد بلدي ThreadFactory
التنفيذ إلى newSingleThreadExecutor()
وتعيين مخصص uncaughtExceptionHandler
S ل Thread
يخرج منه. هناك طريقة أخرى هي التفاف myRunnable
إلى محلي (مجهول) Runnable
يحتوي على تجربة القبض على المحاولة. ربما هناك حلول أخرى مماثلة أيضا. ولكن ... بطريقة ما يشعر هذا القذرة، أشعر أنه لا ينبغي أن يكون هذا معقدا. هل هناك حل نظيف؟
المحلول
الحل النظيف هو استخدام ExecutorService.submit()
بدلا من execute()
. وبعد هذا يعود لك Future
ما يمكنك استخدامه لاسترداد النتيجة أو استثناء المهمة:
ExecutorService executor = Executors.newSingleThreadExecutor();
Runnable task = new Runnable() {
public void run() {
throw new RuntimeException("foo");
}
};
Future<?> future = executor.submit(task);
try {
future.get();
} catch (ExecutionException e) {
Exception rootException = e.getCause();
}
نصائح أخرى
تزيين Runnable في Runnable آخر يمسك باستثناءات وقت التشغيل ومعالجتها:
public class REHandler implements Runnable {
Runnable delegate;
public REHandler (Runnable delegate) {
this.delegate = delegate;
}
public void run () {
try {
delegate.run ();
} catch (RuntimeException e) {
... your fancy error handling here ...
}
}
}
executor.execute(new REHandler (myRunnable));
لماذا لا تتصل ExecutorService#submit()
, ، احصل على ال Future
ثم قم بتعامل مع استثناءات محتملة بنفسك عند الاتصال Future#get()
?
Skaffman صحيحة في ذلك باستخدام submit
هو أنظف النهج. نهج بديل هو الفئة الفرعية ThreadPoolExecutor
وتجاوز afterExecute(Runnable, Throwable)
. وبعد إذا اتبعت هذا النهج تأكد من الاتصال execute(Runnable)
بدلا من submit(Runnable)
أو afterExecute
لن يتم استدعاء.
لكل وصف API:
تم استدعاء الأسلوب عند الانتهاء من تنفيذ Runnable المعطى. يتم استدعاء هذه الطريقة بواسطة مؤشر الترابط الذي أعدم المهمة. إذا لم تكن غير فارغة، فإن الرمي هو غير قديم
RuntimeException
أوError
تسبب هذا التنفيذ لإنهاء فجأة.ملاحظة: عندما تكون الإجراءات المرفقة في المهام (مثل Futuretask) إما بشكل صريح أو عبر طرق مثل الإرسال، فإن هذه الأشياء المهمة تؤتي ثمارها والحفاظ على استثناءات حسابية، وبالتالي فهي لا تسبب إنهاء مفاجئ، والاستثناءات الداخلية لم يتم تمريرها إلى هذه الطريقة.
مهمة(Callable
أو Runnable
) ارسلت الى ThreadPoolExecutors
سيتم تحويل إلى FuturnTask
, ، يحتوي على الدعامة المسماة callable
يساوي المهمة التي تقدمها. futurntask لها خاص بها run
الطريقة كما يلي. كل الاستثناء أو رمي رمي في c.call()
سيتم اصطيادها ووضعها في الدعامة المسماة outcome
. وبعد عند الاتصال FuturnTask get
طريقة، outcome
سيتم رميها
futurntask.run من رمز المصدر JDK1.8
public void run() {
...
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
// save ex into `outcome` prop
setException(ex);
}
if (ran)
set(result);
}
}
...
}
إذا كنت تريد التقاط الاستثناء:
- 1. إجابة Skaffman
- 2. الكتابة فوق `afterexecute` عندما كنت جديد threadpoolexecutor
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
Throwable cause = null;
if (t == null && r instanceof Future) {
try {
((Future<?>) r).get();
} catch (InterruptedException | ExecutionException e) {
cause = e;
}
} else if (t != null) {
cause = t;
}
if (cause != null) {
// log error
}
}