Saya ingin menjalankan utas untuk jangka waktu tertentu. Jika tidak selesai dalam waktu itu, saya ingin membunuhnya, membuang beberapa pengecualian, atau menanganinya dengan cara tertentu. Bagaimana itu bisa dilakukan?
Salah satu cara melakukannya seperti yang saya ketahui dari utas ini adalah dengan menggunakan TimerTask di dalam metode run () dari Thread.
Apakah ada solusi yang lebih baik untuk ini?
EDIT: Menambahkan karunia karena saya membutuhkan jawaban yang lebih jelas. Kode ExecutorService yang diberikan di bawah ini tidak mengatasi masalah saya. Mengapa saya harus tidur () setelah mengeksekusi (beberapa kode - saya tidak memiliki pegangan atas potongan kode ini)? Jika kode selesai dan sleep () terputus, bagaimana bisa itu timeout?
Tugas yang perlu dijalankan tidak dalam kendali saya. Itu bisa berupa potongan kode. Masalahnya adalah bagian kode ini mungkin mengalami infinite loop. Saya tidak ingin itu terjadi. Jadi, saya hanya ingin menjalankan tugas itu di utas terpisah. Utas induk harus menunggu hingga utas itu selesai dan perlu mengetahui status tugas (yaitu apakah waktunya habis atau beberapa pengecualian terjadi atau jika berhasil). Jika tugas masuk ke loop tak terbatas, utas orang tua saya terus menunggu tanpa batas, yang bukan situasi ideal.
sumber
sleep()
hanya sebuah rintisan untuk mewakili "tugas berjalan lama". Ganti saja dengan tugas Anda yang sebenarnya;)interrupt()
panggilan pada utasnya ... tidak semua "memblokir" panggilan, seperti yang saya coba tunjukkan dalam jawaban saya. Spesifik dari tugas yang Anda coba batalkan membuat perbedaan besar dalam pendekatan yang harus digunakan. Informasi lebih lanjut tentang tugas akan sangat membantu.Jawaban:
Memang lebih baik digunakan
ExecutorService
daripadaTimer
, inilah SSCCE :Mainkan sedikit dengan
timeout
argumen dalamFuture#get()
metode, mis. Tingkatkan menjadi 5 dan Anda akan melihat bahwa utas selesai. Anda dapat mencegat batas waktu dicatch (TimeoutException e)
blok.Memperbarui: untuk memperjelas kesalahpahaman konseptual, yang
sleep()
adalah tidak diperlukan. Itu hanya digunakan untuk tujuan SSCCE / demonstrasi. Lakukan saja tugas jangka panjang Anda di sanasleep()
. Di dalam tugas jangka panjang Anda, Anda harus memeriksa apakah utas tidak terputus sebagai berikut:sumber
Thread.sleep(4000)
dengan beberapa pernyataan jangka panjang lainnya dan contohnya tidak akan berfungsi. Dengan kata lain, contoh ini hanya akan berfungsi jikaTask
dirancang untuk memahamiThread.isInterrupted()
perubahan status.Tidak ada 100% cara andal untuk melakukan ini untuk tugas lama apa pun. Tugas harus ditulis dengan kemampuan ini dalam pikiran.
Pustaka Java Inti suka
ExecutorService
membatalkan tugas yang tidak sinkron denganinterrupt()
panggilan pada utas pekerja. Jadi, misalnya, jika tugas tersebut berisi semacam loop, Anda harus memeriksa status interupsi pada setiap iterasi. Jika tugasnya adalah melakukan operasi I / O, mereka juga harus interruptible — dan pengaturannya bisa rumit. Bagaimanapun, ingatlah bahwa kode harus secara aktif memeriksa interupsi; mengatur interupsi tidak selalu melakukan apa pun.Tentu saja, jika tugas Anda adalah loop sederhana, Anda bisa memeriksa waktu saat ini di setiap iterasi dan menyerah ketika batas waktu yang ditentukan telah berlalu. Dalam hal ini tidak diperlukan utas pekerja.
sumber
execute()
Metode antarmuka induk,Executor
dapat menjalankan tugas di utas panggilan. Tidak ada pernyataan serupa untuksubmit()
metodeExecutorService
yang mengembalikanFuture
contoh. Implikasi dari layanan ini adalah bahwa ada utas pekerja yang harus dibersihkan melalui shutdown, dan bahwa tugas dijalankan secara tidak sinkron. Yang mengatakan, tidak ada dalam kontrak yang mengatakan bahwaExecutorService
dilarang melakukan tugas di utas pengajuan; jaminan tersebut berasal dari API implementasi, sepertiExecutors
pabrik.Pertimbangkan untuk menggunakan instance dari ExecutorService . Keduanya
invokeAll()
daninvokeAny()
metode tersedia dengantimeout
parameter.Utas saat ini akan memblokir hingga metode selesai (tidak yakin apakah ini diinginkan) baik karena tugas diselesaikan secara normal atau batas waktu tercapai. Anda dapat memeriksa yang dikembalikan
Future
untuk menentukan apa yang terjadi.sumber
Dengan asumsi kode utas di luar kendali Anda:
Dari dokumentasi Java yang disebutkan di atas:
Intinya:
Pastikan semua utas dapat diinterupsi, atau Anda perlu pengetahuan khusus tentang utas - seperti memiliki bendera untuk ditetapkan. Mungkin Anda bisa meminta tugas diberikan kepada Anda bersama dengan kode yang diperlukan untuk menghentikannya - mendefinisikan antarmuka dengan
stop()
metode. Anda juga dapat memperingatkan ketika Anda gagal menghentikan tugas.sumber
BalusC berkata:
Tetapi jika Anda mengganti
Thread.sleep(4000);
denganfor (int i = 0; i < 5E8; i++) {}
maka itu tidak dikompilasi, karena loop kosong tidak melemparInterruptedException
.Dan agar utas itu bisa terputus, ia perlu melempar
InterruptedException
.Ini sepertinya masalah serius bagi saya. Saya tidak bisa melihat bagaimana menyesuaikan jawaban ini untuk bekerja dengan tugas jangka panjang yang umum.
Diedit untuk menambahkan: Saya mengajukan kembali ini sebagai pertanyaan baru: [ memotong utas setelah waktu yang ditentukan, apakah harus membuang InterruptedException? ]
sumber
Saya pikir Anda harus melihat pada mekanisme penanganan konkurensi yang tepat (utas yang berjalan ke loop tak terbatas tidak terdengar bagus, btw). Pastikan Anda membaca sedikit tentang topik Utas "membunuh" atau "menghentikan" .
Apa yang Anda gambarkan, terdengar sangat mirip "pertemuan", jadi Anda mungkin ingin melihat CyclicBarrier .
Mungkin ada konstruksi lain (seperti menggunakan CountDownLatch misalnya) yang dapat menyelesaikan masalah Anda (satu utas menunggu dengan batas waktu untuk kait, yang lain harus menghitung mundur kait jika telah berhasil, yang akan melepaskan utas pertama Anda setelah batas waktu atau ketika hitung mundur dipanggil).
Saya biasanya merekomendasikan dua buku di bidang ini: Pemrograman Serentak di Jawa dan Java Concurrency dalam Praktek .
sumber
Saya membuat kelas pembantu hanya untuk ini beberapa waktu lalu. Bekerja sangat baik:
Disebut seperti ini:
sumber
Saya memposting Anda sepotong kode yang menunjukkan cara bagaimana menyelesaikan masalah. Sebagai contoh saya membaca file. Anda bisa menggunakan metode ini untuk operasi lain, tetapi Anda perlu mengimplementasikan metode kill () sehingga operasi utama akan terganggu.
semoga membantu
Salam
sumber
Inilah saya yang sangat sederhana untuk menggunakan kelas pembantu untuk menjalankan atau memanggil bagian dari kode Java :-)
Ini didasarkan pada jawaban yang sangat baik dari BalusC
sumber
Cuplikan berikut akan memulai operasi di utas terpisah, lalu tunggu hingga 10 detik hingga operasi selesai. Jika operasi tidak selesai dalam waktu, kode akan berusaha untuk membatalkan operasi, kemudian melanjutkan dengan cara yang menyenangkan. Bahkan jika operasi tidak dapat dibatalkan dengan mudah, utas induk tidak akan menunggu utas anak berakhir.
The
getExecutorService()
Metode dapat diimplementasikan dalam beberapa cara. Jika Anda tidak memiliki persyaratan khusus, Anda dapat memanggilExecutors.newCachedThreadPool()
penggabungan utas tanpa batas atas jumlah utas.sumber
SomeClass
danFuture
?Satu hal yang belum pernah saya lihat disebutkan adalah bahwa membunuh utas umumnya adalah Ide Buruk. Ada beberapa teknik untuk membuat metode berulir dapat dibatalkan dengan bersih , tetapi itu berbeda dengan hanya mematikan utas setelah waktu habis.
Risiko dengan apa yang Anda sarankan adalah bahwa Anda mungkin tidak tahu bagaimana keadaan utas saat Anda membunuhnya - jadi Anda berisiko menimbulkan ketidakstabilan. Solusi yang lebih baik adalah memastikan kode ulir Anda tidak hang sendiri, atau akan merespons permintaan abort dengan baik.
sumber
Jawaban hebat dari BalusC:
tetapi Hanya untuk menambahkan bahwa batas waktu itu sendiri tidak mengganggu utas itu sendiri. bahkan jika Anda memeriksa while (! Thread.interrupted ()) dalam tugas Anda. jika Anda ingin memastikan utas dihentikan, Anda juga harus memastikan future.cancel () dipanggil saat pengecualian batas waktu habis.
sumber
Saya pikir jawabannya terutama tergantung pada tugas itu sendiri.
Jika jawaban pertama adalah ya dan jawaban kedua adalah tidak, Anda dapat membuatnya sesederhana ini:
Jika ini bukan opsi, persempit persyaratan Anda - atau tunjukkan beberapa kode.
sumber
Saya sedang mencari ExecutorService yang dapat mengganggu semua waktu habis Runnables dieksekusi olehnya, tetapi tidak menemukannya. Setelah beberapa jam saya membuat satu seperti di bawah ini. Kelas ini dapat dimodifikasi untuk meningkatkan ketahanan.
Pemakaian:
sumber
Sekarang, saya menemui masalah seperti ini. Itu terjadi pada decode gambar. Proses decode memakan waktu terlalu lama sehingga layar tetap hitam. l menambahkan pengontrol waktu: ketika waktu terlalu lama, lalu muncul dari Utas saat ini. Berikut ini perbedaannya:
sumber
Saya memiliki masalah yang sama. Jadi saya datang dengan solusi sederhana seperti ini.
Menjamin bahwa jika blok tidak dieksekusi dalam batas waktu. proses akan mengakhiri dan melempar pengecualian.
contoh:
sumber
Dalam solusi yang diberikan oleh BalusC , utas utama akan tetap diblokir untuk periode waktu habis. Jika Anda memiliki kumpulan utas dengan lebih dari satu utas, Anda akan membutuhkan jumlah utas tambahan yang sama yang akan menggunakan Future.get (waktu habis, unit TimeUnit) memblokir panggilan untuk menunggu dan menutup utas jika melebihi periode waktu habis.
Solusi umum untuk masalah ini adalah membuat Dekorator ThreadPoolExecutor yang dapat menambahkan fungsionalitas batas waktu. Kelas Dekorator ini harus membuat utas sebanyak yang dimiliki ThreadPoolExecutor, dan semua utas ini hanya digunakan untuk menunggu dan menutup ThreadPoolExecutor.
Kelas generik harus diimplementasikan seperti di bawah ini:
Dekorator di atas dapat digunakan sebagai berikut:
sumber