Apa cara paling sederhana untuk menunggu semua tugas ExecutorService
selesai? Tugas saya terutama komputasi, jadi saya hanya ingin menjalankan sejumlah besar pekerjaan - satu di setiap inti. Sekarang pengaturan saya terlihat seperti ini:
ExecutorService es = Executors.newFixedThreadPool(2);
for (DataTable singleTable : uniquePhrases) {
es.execute(new ComputeDTask(singleTable));
}
try{
es.wait();
}
catch (InterruptedException e){
e.printStackTrace();
}
ComputeDTask
mengimplementasikan runnable. Ini tampaknya menjalankan tugas dengan benar, tetapi kode macet wait()
dengan IllegalMonitorStateException
. Ini aneh, karena saya bermain-main dengan beberapa contoh mainan dan tampaknya berhasil.
uniquePhrases
mengandung beberapa puluh ribu elemen. Haruskah saya menggunakan metode lain? Saya mencari sesuatu yang sesederhana mungkin
java
multithreading
threadpool
executorservice
george tersenyum
sumber
sumber
es
) ketika Anda ingin menunggu - kunci akan dilepaskan secara otomatis sambil menungguExecutors.newFixedThreadPool(System.getRuntime().availableProcessors());
Jawaban:
Pendekatan paling sederhana adalah menggunakan
ExecutorService.invokeAll()
yang melakukan apa yang Anda inginkan dalam satu-liner. Dalam bahasa Anda, Anda harus memodifikasi atau membungkusComputeDTask
untuk mengimplementasikanCallable<>
, yang dapat memberi Anda sedikit lebih banyak fleksibilitas. Mungkin di aplikasi Anda ada implementasi yang bermaknaCallable.call()
, tapi inilah cara untuk membungkusnya jika tidak menggunakanExecutors.callable()
.Seperti yang telah ditunjukkan orang lain, Anda dapat menggunakan versi batas waktu
invokeAll()
jika sesuai. Dalam contoh ini,answers
akan berisi banyakFuture
s yang akan mengembalikan nol (lihat definisiExecutors.callable()
. Mungkin yang ingin Anda lakukan adalah sedikit refactoring sehingga Anda bisa mendapatkan jawaban yang berguna kembali, atau referensi ke yang mendasarinyaComputeDTask
, tapi saya bisa dari contoh Anda.Jika tidak jelas, perhatikan bahwa
invokeAll()
tidak akan kembali sampai semua tugas selesai. (yaitu, semua yang adaFuture
dianswers
koleksi Anda akan melaporkan.isDone()
jika diminta.) Ini menghindari semua shutdown manual, menungguTerminasi, dll ... dan memungkinkan Anda untuk menggunakan kembali iniExecutorService
dengan rapi untuk beberapa siklus, jika diinginkan.Ada beberapa pertanyaan terkait pada SO:
Cara menunggu semua utas selesai
Kembalikan nilai dari utas java
invokeAll () tidak mau menerima Koleksi <Callable <t>>
Apakah saya perlu melakukan sinkronisasi?
Tidak ada yang benar-benar tepat untuk pertanyaan Anda, tetapi mereka memang memberikan sedikit warna tentang bagaimana orang berpikir
Executor
/ExecutorService
seharusnya digunakan.sumber
Jika Anda ingin menunggu semua tugas selesai, gunakan
shutdown
metode sebagai gantiwait
. Kemudian ikuti denganawaitTermination
.Selain itu, Anda dapat menggunakan
Runtime.availableProcessors
untuk mendapatkan jumlah utas perangkat keras sehingga Anda dapat menginisialisasi threadpool dengan benar.sumber
awaitTermination
membutuhkan batas waktu sebagai parameter. Meskipun mungkin untuk memberikan waktu yang terbatas dan menempatkan lingkaran di sekitarnya untuk menunggu sampai semua utas selesai, saya bertanya-tanya apakah ada solusi yang lebih elegan.Jika menunggu semua tugas dalam
ExecutorService
penyelesaian tidak tepat sasaran Anda, melainkan menunggu hingga kumpulan tugas tertentu selesai, Anda dapat menggunakanCompletionService
- khusus, sebuahExecutorCompletionService
.Idenya adalah untuk menciptakan sebuah
ExecutorCompletionService
membungkus AndaExecutor
, mengirimkan beberapa nomor yang dikenal tugas melaluiCompletionService
, kemudian menarik bahwa jumlah yang sama dari hasil dari antrian selesai baik menggunakantake()
(yang blok) ataupoll()
(yang tidak). Setelah Anda menggambar semua hasil yang diharapkan sesuai dengan tugas yang Anda kirimkan, Anda tahu semuanya sudah selesai.Biarkan saya menyatakan ini sekali lagi, karena tidak jelas dari antarmuka: Anda harus tahu berapa banyak hal yang Anda masukkan
CompletionService
untuk mengetahui berapa banyak hal yang harus dicoba. Ini sangat penting terutama dengantake()
metode: panggil sekali saja dan itu akan memblokir utas panggilan Anda sampai beberapa utas lainnya mengirimkan pekerjaan lain ke yang samaCompletionService
.Ada beberapa contoh yang menunjukkan bagaimana menggunakan
CompletionService
dalam buku Jawa Concurrency dalam Praktek .sumber
CompletionService
Jika Anda ingin menunggu layanan pelaksana untuk menyelesaikan eksekusi, panggil
shutdown()
dan kemudian, tungguTerminasi (unit, tipe unit) , misawaitTermination(1, MINUTE)
. ExecutorService tidak memblokir monitor itu sendiri, jadi Anda tidak dapat menggunakanwait
dll.sumber
awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
stackoverflow.com/a/1250655/32453Anda bisa menunggu pekerjaan selesai pada interval tertentu:
Atau Anda bisa menggunakan ExecutorService . kirim ( Runnable ) dan kumpulkan objek Future yang dikembalikan dan panggil get () pada masing-masing pada gilirannya untuk menunggu sampai selesai.
InterruptedException sangat penting untuk ditangani dengan benar. Inilah yang memungkinkan Anda atau pengguna perpustakaan Anda menghentikan proses panjang dengan aman.
sumber
Gunakan saja
Di setiap utas
dan sebagai penghalang
sumber
Penyebab root untuk IllegalMonitorStateException :
Dari kode Anda, Anda baru saja menelepon wait () pada ExecutorService tanpa memiliki kunci.
Kode di bawah ini akan diperbaiki
IllegalMonitorStateException
Ikuti salah satu pendekatan di bawah ini untuk menunggu penyelesaian semua tugas, yang telah diserahkan
ExecutorService
.Iterate melalui semua
Future
tugas darisubmit
padaExecutorService
dan memeriksa status dengan memblokir panggilanget()
padaFuture
objekGunakan invokeAll on
ExecutorService
Menggunakan CountDownLatch
Menggunakan ForkJoinPool atau newWorkStealingPool dari
Executors
(sejak java 8)Matikan pool seperti yang direkomendasikan di halaman dokumentasi oracle
Jika Anda ingin dengan anggun menunggu semua tugas selesai saat Anda menggunakan opsi 5 bukannya opsi 1 hingga 4, ubah
untuk
a
while(condition)
yang memeriksa setiap 1 menit.sumber
Anda dapat menggunakan
ExecutorService.invokeAll
metode, Ini akan menjalankan semua tugas dan menunggu sampai semua utas menyelesaikan tugas mereka.Ini javadoc lengkap
Anda juga dapat menggunakan versi berlebih dari metode ini untuk menentukan batas waktu.
Berikut adalah contoh kode dengan
ExecutorService.invokeAll
sumber
Saya juga memiliki situasi dimana saya memiliki set dokumen untuk dijelajahi. Saya mulai dengan dokumen "seed" awal yang harus diproses, dokumen itu berisi tautan ke dokumen lain yang juga harus diproses, dan seterusnya.
Di program utama saya, saya hanya ingin menulis sesuatu seperti yang berikut, di mana
Crawler
mengontrol banyak utas.Situasi yang sama akan terjadi jika saya ingin menavigasi pohon; saya akan muncul di root node, prosesor untuk setiap node akan menambahkan anak-anak ke antrian seperlunya, dan banyak thread akan memproses semua node di pohon, sampai tidak ada lagi.
Saya tidak dapat menemukan apa pun di JVM yang saya pikir agak mengejutkan. Jadi saya menulis kelas
ThreadPool
yang bisa digunakan secara langsung atau subclass untuk menambahkan metode yang cocok untuk domain, misalnyaschedule(Document)
. Semoga ini bisa membantu!ThreadPool Javadoc | Maven
sumber
Tambahkan semua utas dalam koleksi dan kirimkan dengan menggunakan
invokeAll
. Jika Anda dapat menggunakaninvokeAll
metodeExecutorService
, JVM tidak akan melanjutkan ke baris berikutnya sampai semua utas selesai.Di sini ada contoh yang bagus: invokeAll via ExecutorService
sumber
Kirim tugas Anda ke dalam Runner dan kemudian tunggu memanggil metode waitTillDone () seperti ini:
Untuk menggunakannya tambahkan dependensi gradle / maven ini:
'com.github.matejtymes:javafixes:1.0'
Untuk lebih jelasnya lihat di sini: https://github.com/MatejTymes/JavaFixes atau di sini: http://matejtymes.blogspot.com/2016/04/executor-that-notifies-you-when-task.html
sumber
Alternatif sederhana untuk ini adalah menggunakan utas bersama dengan bergabung. Refer: Menggabung Thread
sumber
Saya hanya akan menunggu sampai eksekutor berakhir dengan batas waktu yang ditentukan yang menurut Anda cocok untuk menyelesaikan tugas-tugas tersebut.
sumber
Kedengarannya seperti yang Anda butuhkan
ForkJoinPool
dan gunakan kumpulan global untuk menjalankan tugas.Keindahan adalah di
pool.awaitQuiescence
mana metode akan memblokir memanfaatkan utas penelepon untuk melaksanakan tugasnya dan kemudian kembali ketika itu benar - benar kosong.sumber