Saya mencoba menggunakan ThreadPoolExecutor
kelas Java untuk menjalankan sejumlah besar tugas berat dengan sejumlah utas. Setiap tugas memiliki banyak tempat di mana mungkin gagal karena pengecualian.
Saya telah mensubklasifikasikan ThreadPoolExecutor
dan mengganti afterExecute
metode yang seharusnya memberikan pengecualian tanpa tertangkap saat menjalankan tugas. Namun, sepertinya saya tidak bisa membuatnya bekerja.
Sebagai contoh:
public class ThreadPoolErrors extends ThreadPoolExecutor {
public ThreadPoolErrors() {
super( 1, // core threads
1, // max threads
1, // timeout
TimeUnit.MINUTES, // timeout units
new LinkedBlockingQueue<Runnable>() // work queue
);
}
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if(t != null) {
System.out.println("Got an error: " + t);
} else {
System.out.println("Everything's fine--situation normal!");
}
}
public static void main( String [] args) {
ThreadPoolErrors threadPool = new ThreadPoolErrors();
threadPool.submit(
new Runnable() {
public void run() {
throw new RuntimeException("Ouch! Got an error.");
}
}
);
threadPool.shutdown();
}
}
Output dari program ini adalah "Semuanya baik-baik saja - situasi normal!" meskipun satu-satunya Runnable yang dikirimkan ke thread pool memberikan pengecualian. Adakah petunjuk tentang apa yang terjadi di sini?
Terima kasih!
Jawaban:
Dari dokumen :
Saat Anda mengirim Runnable, itu akan dibungkus dalam Masa Depan.
AfterExecute Anda harus seperti ini:
sumber
future.isDone()
? KarenaafterExecute
dijalankan setelahRunnable
selesai, saya menganggapfuture.isDone()
selalu kembalitrue
.PERINGATAN : Perlu dicatat bahwa solusi ini akan memblokir utas panggilan.
Jika Anda ingin memproses pengecualian yang dilemparkan oleh tugas, maka umumnya lebih baik digunakan
Callable
daripadaRunnable
.Callable.call()
diizinkan untuk melemparkan pengecualian yang diperiksa, dan ini dapat disebarkan kembali ke utas panggilan:Jika
Callable.call()
melempar pengecualian, ini akan dibungkus denganExecutionException
dan dibuangFuture.get()
.Ini mungkin lebih disukai daripada subklasifikasi
ThreadPoolExecutor
. Ini juga memberi Anda kesempatan untuk mengirimkan kembali tugas jika pengecualiannya dapat dipulihkan.sumber
future.get()
atau versi kelebihannya dipanggil.Penjelasan untuk perilaku ini tepat di javadoc untuk afterExecute :
sumber
Saya mengatasinya dengan membungkus runnable yang disediakan yang diserahkan kepada pelaksana.
sumber
whenComplete()
metodeCompletableFuture
.Saya menggunakan
VerboseRunnable
kelas dari jcabi-log , yang menelan semua pengecualian dan mencatatnya. Sangat mudah, misalnya:sumber
Solusi lain adalah dengan menggunakan ManagedTask dan ManagedTaskListener .
Anda memerlukan Callable atau Runnable yang mengimplementasikan antarmuka ManagedTask .
Metode
getManagedTaskListener
mengembalikan contoh yang Anda inginkan.Dan Anda menerapkan di ManagedTaskListener yang
taskDone
metode:Lebih detail tentang siklus hidup dan pendengar tugas yang dikelola .
sumber
Ini bekerja
Ini akan membuat Executor dengan utas tunggal, yang bisa mendapatkan banyak tugas; dan akan menunggu saat ini untuk mengakhiri eksekusi untuk memulai dengan yang berikutnya
Jika terjadi kesalahan atau pengecualian uncaugth, uncaughtExceptionHandler akan menangkapnya
sumber
Jika Anda ingin memantau pelaksanaan tugas, Anda dapat memutar 1 atau 2 utas (mungkin lebih tergantung pada beban) dan menggunakannya untuk mengambil tugas dari bungkus ExecutionCompletionService.
sumber
Jika Anda
ExecutorService
berasal dari sumber eksternal (yaitu tidak mungkin untuk subkelasThreadPoolExecutor
dan menimpanyaafterExecute()
), Anda dapat menggunakan proxy dinamis untuk mencapai perilaku yang diinginkan:sumber
Ini karena
AbstractExecutorService :: submit
sedang membungkus Andarunnable
keRunnableFuture
(tidak lain kecualiFutureTask
) seperti di bawah iniKemudian
execute
akan meneruskannya keWorker
danWorker.run()
akan memanggil di bawah ini.sumber
Ini mirip dengan solusi mmm, tetapi sedikit lebih bisa dimengerti. Mintalah tugas Anda memperluas kelas abstrak yang membungkus metode run ().
sumber
Alih-alih mensubklasifikasikan ThreadPoolExecutor, saya akan menyediakannya dengan instance ThreadFactory yang membuat Thread baru dan memberi mereka UncaughtExceptionHandler
sumber