[改善Java代码]不使用stop方法停止线程
線程啟動(dòng)完畢后,在運(yùn)行可能需要終止,Java提供的終止方法只有一個(gè)stop,但是不建議使用此方法,因?yàn)樗幸韵氯齻€(gè)問(wèn)題:
(1)stop方法是過(guò)時(shí)的
從Java編碼規(guī)則來(lái)說(shuō),已經(jīng)過(guò)時(shí)的方式不建議采用.
(2)stop方法會(huì)導(dǎo)致代碼邏輯不完整
stop方法是一種"惡意" 的中斷,一旦執(zhí)行stop方法,即終止當(dāng)前正在運(yùn)行的線程,不管線程邏輯是否完整,這是非常危險(xiǎn)的.
看如下代碼:
1 public class Client { 2 public static void main(String[] args) throws Exception { 3 // 子線程 4 Thread thread = new Thread() { 5 @Override 6 public void run() { 7 try { 8 // 該線程休眠1秒 9 Thread.sleep(1000); 10 } catch (InterruptedException e) { 11 //異常處理 12 } 13 System.out.println("此處代碼不會(huì)執(zhí)行"); 14 } 15 }; 16 // 啟動(dòng)線程 17 thread.start(); 18 // 主線程休眠0.1秒 19 Thread.sleep(100); 20 // 子線程停止 21 thread.stop(); 22 23 } 24 }?
這段代碼的邏輯,子線程是一個(gè)匿名內(nèi)部類(lèi),它的run方法在執(zhí)行時(shí)會(huì)休眠1秒鐘,然后再執(zhí)行后續(xù)的邏輯,而主線程則是休眠0.1秒后終止子線程的運(yùn)行,也就是說(shuō),JVM在執(zhí)行thread.stop()時(shí),子線程還在執(zhí)行sleep(1000),此時(shí)stop方法會(huì)清除棧內(nèi)信息,結(jié)束該線程,這也導(dǎo)致了run方法的邏輯不完整,輸出語(yǔ)句println代表的是一段邏輯,可能非常重要,比如子線程的主邏輯,資源回收,情景初始化等等,但是因?yàn)閟top線程了,這些就都不再執(zhí)行了,于是就產(chǎn)生了業(yè)務(wù)邏輯不完整的情況.
這是極度危險(xiǎn)的,因?yàn)槲覀儾恢雷泳€程會(huì)在什么時(shí)候停止,stop連基本的邏輯完整性都無(wú)法保證,而且此種操作也是非常隱蔽的,子線程執(zhí)行到何處會(huì)被關(guān)閉很難定位,這為以后的維護(hù)帶來(lái)了很多的麻煩.?
(3)stop方法會(huì)破壞原子邏輯
多線程為了解決共享資源搶占的問(wèn)題,使用了鎖的概念,避免資源不同步,但是正是因?yàn)榇嗽?stop方法卻會(huì)帶來(lái)更大的麻煩,它會(huì)丟棄所有的鎖,導(dǎo)致原子邏輯受損.例如 有這樣一段程序:
1 public class Client { 2 public static void main(String[] args) { 3 MultiThread t = new MultiThread(); 4 Thread t1 = new Thread(t); 5 // 啟動(dòng)t1線程 6 t1.start(); 7 for (int i = 0; i < 5; i++) { 8 new Thread(t).start(); 9 } 10 // 停止t1線程 11 t1.stop(); 12 } 13 } 14 15 class MultiThread implements Runnable { 16 int a = 0; 17 18 @Override 19 public void run() { 20 // 同步代碼塊,保證原子操作 21 synchronized ("") { 22 // 自增 23 a++; 24 try { 25 // 線程休眠0.1秒 26 Thread.sleep(100); 27 } catch (InterruptedException e) { 28 e.printStackTrace(); 29 } 30 // 自減 31 a--; 32 String tn = Thread.currentThread().getName(); 33 System.out.println(tn + ":a =" + a); 34 } 35 } 36 }?
?MultiThread實(shí)現(xiàn)了Runnable接口,具備多線程的能力,run方法中加入了synchronized代碼塊,表示內(nèi)部是原子邏輯,a的值會(huì)先增加后減少,按照synchronized的規(guī)則,無(wú)論啟動(dòng)多少個(gè)線程,打印出來(lái)的結(jié)果都應(yīng)該是a=0,
但是如果有一個(gè)正在執(zhí)行的線程被stop,就會(huì)破壞這種原子邏輯.(上面main方法中代碼)
首先說(shuō)明的是所有線程共享了 一個(gè)MultThread的實(shí)例變量t,其次由于在run方法中加入了同步代碼塊,所以只能有一個(gè)線程進(jìn)入到synchronized塊中.
此段代碼的執(zhí)行順序如下:
1)線程t1啟動(dòng),并執(zhí)行run方法,由于沒(méi)有其他線程同步代碼塊的鎖,所以t1線程執(zhí)行自加后執(zhí)行到sleep方法開(kāi)始休眠,此時(shí)a=1.
2)JVM又啟動(dòng)了5個(gè)線程,也同時(shí)運(yùn)行run方法,由于synchronized關(guān)鍵字的阻塞作用,這5個(gè)線程不能執(zhí)行自增和自減操作,等待t1線程釋放線程鎖.
3)主線程執(zhí)行了t1.stop方法,終止了t1線程,注意由于a變量是線程共享的,所以其他5個(gè)線程獲得的a變量也是1.
4)其他5個(gè)線程獲得CPU的執(zhí)行機(jī)會(huì),打印出a的值.
結(jié)果是:
Thread-5:a =1 Thread-4:a =1 Thread-3:a =1 Thread-2:a =1 Thread-1:a =1?
原本期望synchronized同步代碼塊中的邏輯都是原子邏輯,不受外界線程的干擾,但是結(jié)果卻出現(xiàn)原子邏輯被破壞的情況,這也是stop方法被廢棄的一個(gè)重要原因:破壞了原子邏輯.
既然終止一個(gè)線程不能用stop方法,那怎樣才能終止一個(gè)正在運(yùn)行的線程呢?
使用自定義的標(biāo)志位決定線程的執(zhí)行情況,代碼如下:
1 import java.util.Timer; 2 import java.util.TimerTask; 3 4 public class Client { 5 public static void main(String[] args) throws InterruptedException { 6 final SafeStopThread sst = new SafeStopThread(); 7 sst.start(); 8 //0.5秒后線程停止執(zhí)行 9 new Timer(true).schedule(new TimerTask() { 10 public void run() { 11 sst.terminate(); 12 } 13 }, 500); 14 } 15 16 } 17 18 class SafeStopThread extends Thread { 19 //此變量必須加上volatile 20 private volatile boolean stop = false; 21 @Override 22 public void run() { 23 //判斷線程體是否運(yùn)行 24 while (stop) { 25 // Do Something 26 System.out.println("Stop"); 27 } 28 } 29 //線程終止 30 public void terminate() { 31 stop = true; 32 } 33 }?
在線程主題中判斷是否需要停止運(yùn)行,即可保證線程體的邏輯完整性而且也不會(huì)破壞原值邏輯.
Thread還提供了一個(gè)interrupt中斷線程的方法,這個(gè)不是過(guò)時(shí)的方法,是否可以使用這個(gè)中斷線程?
很明確的說(shuō),interrupt不能終止一個(gè)正在執(zhí)行著的線程,它只是修改中斷標(biāo)志位而已.例如:
1 public class Client { 2 public static void main(String[] args) { 3 Thread t1 = new Thread() { 4 public void run() { 5 //線程一直運(yùn)行 6 while (true) { 7 System.out.println("Running……"); 8 } 9 } 10 }; 11 // 啟動(dòng)t1線程 12 t1.start(); 13 System.out.println(t1.isInterrupted());//false 14 // 中斷t1線程 15 t1.interrupt(); 16 System.out.println(t1.isInterrupted());//true 17 } 18 }?
執(zhí)行這段代碼,會(huì)一直有Running在輸出,永遠(yuǎn)不會(huì)停止,執(zhí)行了interrupt沒(méi)有任何的變化,那是因?yàn)閕nterrupt方法不能終止一個(gè)線程狀態(tài),它只會(huì)改變中斷標(biāo)志位.
在t1.interrupt()前后加上了t1.isInterrupted()會(huì)發(fā)現(xiàn)分別輸出的是false和true.
如果需要終止該線程,還需要執(zhí)行進(jìn)行判斷,例如我們可以使用interrupt編寫(xiě)出更加簡(jiǎn)潔,安全的終止線程的代碼:
1 import java.util.Timer; 2 import java.util.TimerTask; 3 4 public class Client { 5 public static void main(String[] args) throws InterruptedException { 6 final SafeStopThread sst = new SafeStopThread(); 7 sst.start(); 8 //0.5秒后線程停止執(zhí)行 9 new Timer(true).schedule(new TimerTask() { 10 public void run() { 11 sst.interrupt(); 12 } 13 }, 500); 14 } 15 16 } 17 18 class SafeStopThread extends Thread { 19 @Override 20 public void run() { 21 //判斷線程體是否運(yùn)行 22 while (!isInterrupted()) { 23 // Do Something 24 } 25 } 26 }?
總之,如果期望終止一個(gè)正在運(yùn)行的線程,則不能使用已經(jīng)過(guò)時(shí)的stop方法,需要執(zhí)行編碼實(shí)現(xiàn).這樣保證原子邏輯不被破壞,代碼邏輯不會(huì)出現(xiàn)異常.
當(dāng)然還可以使用線程池,比如ThreadPoolExecutor類(lèi),那么可以通過(guò)shutdown方法逐步關(guān)閉線程池中的線程,它采用的是比較溫和,安全的關(guān)閉線程方法,完全不會(huì)產(chǎn)生類(lèi)似stop方法的弊端.
?
轉(zhuǎn)載于:https://www.cnblogs.com/DreamDrive/p/5623804.html
總結(jié)
以上是生活随笔為你收集整理的[改善Java代码]不使用stop方法停止线程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: DockerCon 2016 深度解读:
- 下一篇: [改善Java代码]使用valueOf前