Saya perlu solusi untuk menghentikan thread dengan benar di Jawa.
Saya memiliki IndexProcessor
kelas yang mengimplementasikan antarmuka Runnable:
public class IndexProcessor implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class);
@Override
public void run() {
boolean run = true;
while (run) {
try {
LOGGER.debug("Sleeping...");
Thread.sleep((long) 15000);
LOGGER.debug("Processing");
} catch (InterruptedException e) {
LOGGER.error("Exception", e);
run = false;
}
}
}
}
Dan saya memiliki ServletContextListener
kelas yang memulai dan menghentikan utas:
public class SearchEngineContextListener implements ServletContextListener {
private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class);
private Thread thread = null;
@Override
public void contextInitialized(ServletContextEvent event) {
thread = new Thread(new IndexProcessor());
LOGGER.debug("Starting thread: " + thread);
thread.start();
LOGGER.debug("Background process successfully started.");
}
@Override
public void contextDestroyed(ServletContextEvent event) {
LOGGER.debug("Stopping thread: " + thread);
if (thread != null) {
thread.interrupt();
LOGGER.debug("Thread successfully stopped.");
}
}
}
Tetapi ketika saya mematikan kucing jantan, saya mendapatkan pengecualian di kelas IndexProcessor saya:
2012-06-09 17:04:50,671 [Thread-3] ERROR IndexProcessor Exception
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at lt.ccl.searchengine.processor.IndexProcessor.run(IndexProcessor.java:22)
at java.lang.Thread.run(Unknown Source)
Saya menggunakan JDK 1.6. Jadi pertanyaannya adalah:
Bagaimana saya bisa menghentikan utas dan tidak membuang pengecualian?
PS Saya tidak ingin menggunakan .stop();
metode karena sudah usang.
java
multithreading
listener
Paulius Matulionis
sumber
sumber
InterruptedException
. Inilah yang saya pikirkan, tetapi saya juga bertanya-tanya bagaimana cara standarnya.InterruptedException
dapat ditemukan di ibm.com/developerworks/library/j-jtp05236 .Jawaban:
Di
IndexProcessor
kelas Anda memerlukan cara mengatur bendera yang menginformasikan utas bahwa ia harus diakhiri, mirip dengan variabelrun
yang telah Anda gunakan hanya dalam lingkup kelas.Ketika Anda ingin menghentikan utas, Anda mengatur bendera ini dan memanggil
join()
utas dan menunggu sampai selesai.Pastikan bahwa flag tersebut aman dengan menggunakan variabel volatile atau dengan menggunakan metode pengambil dan penyetel yang disinkronkan dengan variabel yang digunakan sebagai bendera.
Kemudian di
SearchEngineContextListener
:sumber
Menggunakan
Thread.interrupt()
adalah cara yang bisa diterima untuk melakukan ini. Bahkan, mungkin lebih disukai daripada bendera seperti yang disarankan di atas. Alasannya adalah bahwa jika Anda berada di panggilan pemblokiran interruptable (sukaThread.sleep
atau menggunakan operasi java.nio Channel), Anda benar-benar dapat keluar dari mereka segera.Jika Anda menggunakan bendera, Anda harus menunggu operasi pemblokiran selesai dan kemudian Anda dapat memeriksa bendera Anda. Dalam beberapa kasus Anda tetap harus melakukan ini, seperti menggunakan standar
InputStream
/OutputStream
yang tidak dapat disela.Dalam hal itu, ketika utas terputus, itu tidak akan mengganggu IO, namun, Anda dapat dengan mudah melakukan ini secara rutin dalam kode Anda (dan Anda harus melakukan ini di titik-titik strategis di mana Anda dapat berhenti dan membersihkan dengan aman)
Seperti yang saya katakan, keuntungan utama
Thread.interrupt()
adalah bahwa Anda dapat segera keluar dari panggilan interupsi, yang tidak dapat Anda lakukan dengan pendekatan bendera.sumber
interrupt()
mungkin baik-baik saja, tetapi dalam banyak kasus lain tidak (misalnya jika sumber daya perlu ditutup). Jika ada yang mengubah cara kerja dalam loop, Anda harus ingat untuk mengubahinterrupt()
ke cara boolean. Saya akan mencari cara yang aman dari awal dan menggunakan bendera.Jawaban sederhana: Anda dapat menghentikan utas secara internal dengan salah satu dari dua cara umum:
Anda juga dapat menghentikan utas SECARA EKSTERNAL:
system.exit
(ini membunuh seluruh proses Anda)interrupt()
*kill()
ataustop()
)*: Harapannya adalah bahwa ini seharusnya menghentikan utas. Namun, apa yang sebenarnya dilakukan utas saat ini terjadi sepenuhnya tergantung pada apa yang ditulis pengembang saat mereka membuat implementasi utas.
Pola umum yang Anda lihat dengan implementasi metode run adalah a
while(boolean){}
, di mana boolean biasanya bernama sesuatuisRunning
, itu adalah variabel anggota dari kelas utasnya, itu mudah berubah, dan biasanya dapat diakses oleh utas lain dengan metode penyetel macam, misalnyakill() { isRunnable=false; }
. Subrutin-subrutin ini bagus karena memungkinkan utas untuk melepaskan sumber daya yang dimilikinya sebelum diakhiri.sumber
Anda harus selalu mengakhiri utas dengan memeriksa bendera di
run()
loop (jika ada).Utas Anda akan terlihat seperti ini:
Kemudian Anda dapat mengakhiri utas dengan menelepon
thread.stopExecuting()
. Dengan begitu utas berakhir bersih, tetapi ini membutuhkan waktu hingga 15 detik (karena Anda tidur). Anda masih dapat memanggil thread.interrupt () jika itu benar-benar mendesak - tetapi cara yang lebih disukai harus selalu memeriksa bendera.Untuk menghindari menunggu selama 15 detik, Anda dapat membagi tidur seperti ini:
sumber
Thread
- itu mengimplementasikanRunnable
- Anda tidak dapat memanggilThread
metode di atasnya kecuali Anda menyatakannya sebagaiThread
dalam hal mana Anda tidak dapat memanggilstopExecuting()
Biasanya, utas diakhiri ketika terputus. Jadi, mengapa tidak menggunakan boolean asli? Coba Terinterupsi ():
ref- Bagaimana saya bisa membunuh utas? tanpa menggunakan stop ();
sumber
Untuk menyinkronkan utas, saya lebih suka menggunakan
CountDownLatch
utas yang membantu menunggu sampai proses yang dilakukan selesai. Dalam hal ini, kelas pekerja diatur denganCountDownLatch
instance dengan jumlah yang diberikan. Panggilan keawait
metode akan diblokir hingga hitungan saat ini mencapai nol karena doacountDown
metode atau set timeout tercapai. Pendekatan ini memungkinkan menginterupsi utas secara instan tanpa harus menunggu waktu tunggu yang ditentukan untuk berlalu:Saat Anda ingin menyelesaikan eksekusi utas lainnya, jalankan countDown di
CountDownLatch
danjoin
utas ke utas utama:sumber
Beberapa info tambahan. Kedua flag dan interrupt disarankan dalam Java doc.
https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
Untuk utas yang menunggu dalam waktu lama (mis., Untuk input), gunakan
Thread.interrupt
sumber
Saya tidak mendapatkan interupsi untuk bekerja di Android, jadi saya menggunakan metode ini, bekerja dengan sempurna:
sumber
volatile
. Lihat docs.oracle.com/javase/specs/jls/se9/html/jls-17.html#jls-17.3 .Kadang saya akan mencoba 1000 kali di onDestroy () / contextDestroyed () saya
sumber