Ditanya cara menamatkan thread, mungkin kebanyakan orang tahu bahawa anda boleh memanggil kaedah Thread.stop.
Tetapi kaedah ini tidak lagi disyorkan selepas jdk1.2. Mengapa ia tidak disyorkan?
Mari kita lihat dulu definisi kaedah ini:
@Deprecated(since="1.2") public final void stop() { @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) { checkAccess(); if (this != Thread.currentThread()) { security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); } } // A zero status value corresponds to "NEW", it can't change to // not-NEW because we hold the lock. if (threadStatus != 0) { resume(); // Wake up thread if it was suspended; no-op otherwise } // The VM can handle all thread states stop0(new ThreadDeath()); }
Daripada kod tersebut, kita dapat melihat bahawa kaedah henti terlebih dahulu menyemak sama ada terdapat kebenaran capaian benang. Jika anda mempunyai kebenaran, tentukan sama ada utas semasa ialah utas yang baru dibuat. Jika tidak, maka hubungi kaedah resume untuk melepaskan keadaan yang digantung.
Akhirnya panggil kaedah stop0 untuk menamatkan urutan.
Antaranya, resume dan stop0 adalah dua kaedah asli, dan pelaksanaan khusus tidak akan dibincangkan di sini.
Nampaknya kaedah berhenti itu munasabah dan tiada masalah. Jadi mengapa kaedah ini tidak selamat?
Seterusnya mari kita lihat contoh.
Kami mencipta kelas NumberCounter Kelas ini mempunyai kaedah peningkatanNumber yang selamat, yang digunakan untuk menambah satu pada nombor:
public class NumberCounter { //要保存的数字 private volatile int number=0; //数字计数器的逻辑是否完整 private volatile boolean flag = false; public synchronized int increaseNumber() throws InterruptedException { if(flag){ //逻辑不完整 throw new RuntimeException("逻辑不完整,数字计数器未执行完毕"); } //开始执行逻辑 flag = true; //do something Thread.sleep(5000); number++; //执行完毕 flag=false; return number; } }
Malah, dalam kerja sebenar, kaedah sedemikian mungkin. diperlukan Ia mengambil masa yang lama untuk dilaksanakan, jadi di sini kami mensimulasikan operasi yang memakan masa ini dengan memanggil Thread.sleep.
Di sini kami juga mempunyai parameter bendera untuk menandakan sama ada kaedah increaseNumber telah berjaya dilaksanakan.
Baiklah, seterusnya kita panggil kaedah kelas ini dalam utas dan lihat apa yang berlaku:
public static void main(String[] args) throws InterruptedException { NumberCounter numberCounter= new NumberCounter(); Thread thread = new Thread(()->{ while (true){ try { numberCounter.increaseNumber(); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread.start(); Thread.sleep(3000); thread.stop(); numberCounter.increaseNumber(); }
Di sini, kami mencipta utas dan tunggu sehingga utas ini berjalan 3 Saat kemudian, kaedah thread.stop dipanggil terus dan kami mendapati pengecualian berikut:
Pengecualian dalam thread "main" java.lang.RuntimeException: Logik tidak lengkap dan pembilang digital belum dilaksanakan.
di com.flydean.NumberCounter.increaseNumber(NumberCounter.java:12)
di com.flydean.Main.main(Main.java:18)
Ini kerana benang. Kaedah berhenti secara langsung menamatkan larian benang, menyebabkan mberCounter.increaseNumber tidak dapat diselesaikan.
Tetapi keadaan belum selesai ini disembunyikan Jika anda menggunakan kaedah thread.stop untuk menamatkan thread, ia berkemungkinan membawa kepada hasil yang tidak diketahui.
Jadi, kami katakan thread.stop tidak selamat.
Jadi, jika kaedah thread.stop tidak dipanggil, bagaimanakah kita boleh menamatkan benang dengan selamat?
Keselamatan yang dipanggil bermakna logik dalam benang perlu dilaksanakan sepenuhnya, bukan separuh dilaksanakan.
Untuk mencapai kesan ini, Thread memberikan kita tiga kaedah yang serupa, iaitu interrupted, interrupted dan isInterrupted.
interrupted adalah untuk menetapkan bendera interrupted untuk thread yang terganggu adalah untuk mengesan gangguan dan mengosongkan status interrupted hanya mengesan gangguan; Satu lagi perkara penting ialah interrupted ialah kaedah kelas yang bertindak pada thread semasa dan isInterrupted bertindak pada thread ini, iaitu, thread yang diwakili oleh contoh yang memanggil kaedah ini dalam kod.
gangguan ialah kaedah gangguan adalah seperti berikut:
Jika contoh urutan semasa memanggil tunggu(), tunggu(lama) atau tunggu( lama, kaedah int) atau kaedah join(), join(long), join(panjang, int) atau kaedah Thread.sleep(panjang) atau Thread.sleep(panjang, int) dipanggil dalam kejadian ini dan sedang menyekat keadaan, statusnya yang terganggu akan dikosongkan dan InterruptedException akan diterima.
Jika urutan ini disekat semasa operasi I/O pada InterruptibleChannel, saluran akan ditutup, status gangguan urutan akan ditetapkan kepada benar dan utas A java.nio .channels.ClosedByInterruptException pengecualian akan diterima.
Jika urutan ini disekat dalam java.nio.channels.Selector, status gangguan urutan akan ditetapkan kepada benar dan ia akan kembali serta-merta daripada operasi pilih .
Jika tiada syarat di atas adalah benar, tetapkan status gangguan kepada benar.
Dalam contoh di atas, dalam kaedah increaseNumber NumberCounter, kami memanggil kaedah Thread.sleep, jadi jika kaedah interrupt thread dipanggil pada masa ini, thread akan membuang An InterruptedException.
Mari tukar contoh panggilan di atas kepada yang berikut:
public static void main(String[] args) throws InterruptedException { NumberCounter numberCounter = new NumberCounter(); Thread thread = new Thread(() -> { while (true) { try { numberCounter.increaseNumber(); } catch (InterruptedException e) { System.out.println("捕获InterruptedException"); throw new RuntimeException(e); } } }); thread.start(); Thread.sleep(500); thread.interrupt(); numberCounter.increaseNumber(); }
Cuba lagi selepas berjalan:
Exception in thread "main" Exception in thread "Thread-0" java.lang.RuntimeException: 逻辑不完整,数字计数器未执行完毕
at com.flydean.NumberCounter.increaseNumber(NumberCounter.java:12)
at com.flydean.Main2.main(Main2.java:21)
java.lang.RuntimeException: java.lang.thread.interrupt: sleep interrupted
at com.flydean.Main2.lambda$main$0(Main2.java:13)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at com.flydean.NumberCounter.increaseNumber(NumberCounter.java:17)
at com.flydean.Main2.lambda$main$0(Main2.java:10)
... 1 more
捕获InterruptedException
可以看到,我们捕获到了这个InterruptedException,并且得知具体的原因是sleep interrupted。
从上面的分析可以得知,thread.stop跟thread.interrupt的表现机制是不一样的。thread.stop属于悄悄终止,我们程序不知道,所以会导致数据不一致,从而产生一些未知的异常。
而thread.interrupt会显示的抛出InterruptedException,当我们捕捉到这个异常的时候,我们就知道线程里面的逻辑在执行的过程中受到了外部作用的干扰,那么我们就可以执行一些数据恢复或者数据校验的动作。
在上面的代码中,我们是捕获到了这个异常,打印出异常日志,然后向上抛出一个RuntimeException。
正常情况下我们是需要在捕获异常之后,进行一些处理。
那么自己处理完这个异常之后,是不是就完美了呢?
答案是否定的。
因为如果我们自己处理了这个InterruptedException, 那么程序中其他部分如果有依赖这个InterruptedException的话,就可能会出现数据不一致的情况。
所以我们在自己处理完InterruptedException之后,还需要再次抛出这个异常。
怎么抛出InterruptedException异常呢?
有两种方式,第一种就是在调用Thread.interrupted()清除了中断标志之后立即抛出:
if (Thread.interrupted()) // Clears interrupted status! throw new InterruptedException();
还有一种方式就是,在捕获异常之后,调用Thread.currentThread().interrupt()再次中断线程。
public void run () { try { while (true) { // do stuff } }catch (InterruptedException e) { LOGGER.log(Level.WARN, "Interrupted!", e); // Restore interrupted state... Thread.currentThread().interrupt(); } }
这两种方式都能达到预想的效果。
Atas ialah kandungan terperinci Bagaimana untuk menamatkan benang di Jawa. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!