- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) >
- Java中線(xiàn)程中斷機制的原理是什么
本篇文章為大家展示了Java中線(xiàn)程中斷機制的原理是什么,內容簡(jiǎn)明扼要并且容易理解,絕對能使你眼前一亮,通過(guò)這篇文章的詳細介紹希望你能有所收獲。
Thread.interrupt真的能中斷線(xiàn)程嗎
在平時(shí)的開(kāi)發(fā)過(guò)程中,相信都會(huì )使用到多線(xiàn)程,在使用多線(xiàn)程時(shí),大家也會(huì )遇到各種各樣的問(wèn)題,今天我們就來(lái)說(shuō)說(shuō)一個(gè)多線(xiàn)程的問(wèn)題——線(xiàn)程中斷。在 java中啟動(dòng)線(xiàn)程非常容易,大多數情況下我是讓一個(gè)線(xiàn)程執行完自己的任務(wù)然后自己停掉,但是有時(shí)候我們需要取消某個(gè)操作,比如你在網(wǎng)絡(luò )下載時(shí),有時(shí)候需 要取消下載。實(shí)現線(xiàn)程的安全中斷并不是一件容易的事情,因為Java并不支持安全快速中斷線(xiàn)程的機制,這里估計很多同學(xué)就會(huì )說(shuō)了,java不是提供了Thread.interrupt
方法中斷線(xiàn)程嗎,好吧,我們今天就從這個(gè)方法開(kāi)始說(shuō)起。
但是調用此方法線(xiàn)程真的會(huì )停止嗎?我們寫(xiě)個(gè)demo看看就知道了。
public class Main { private static final String TAG = "Main"; public static void main(String[] args) { Thread t=new Thread(new NRunnable()); t.start(); System.out.println("is start......."); try { Thread.sleep(3000); } catch (InterruptedException e) { } t.interrupt(); System.out.println("is interrupt......."); } public static class NRunnable implements Runnable { @Override public void run() { while(true) { System.out.println("我沒(méi)有種中斷"); try { Thread.sleep(1000); } catch (InterruptedException e) { } } } } }
如果interrutp方法能夠中斷線(xiàn)程,那么在打印了is interrupt…….之后應該是沒(méi)有log了,我們看看執行結果吧
is start.......
我沒(méi)有種中斷
我沒(méi)有種中斷
我沒(méi)有種中斷
我沒(méi)有種中斷
我沒(méi)有種中斷
is interrupt.......
我沒(méi)有種中斷
我沒(méi)有種中斷
我沒(méi)有種中斷
我沒(méi)有種中斷
我沒(méi)有種中斷
....
通過(guò)結果可以發(fā)現子線(xiàn)程并沒(méi)有中斷
所以 Thread.interrupt()
方法并不能中斷線(xiàn)程,該方法僅僅告訴線(xiàn)程外部已經(jīng)有中斷請求,至于是否中斷還取決于線(xiàn)程自己。在Thread類(lèi)中除了interrupt()
方法還有另外兩個(gè)非常相似的方法:interrupted
和 isInterrupted
方法,下面來(lái)對這幾個(gè)方法進(jìn)行說(shuō)明:
interrupt
此方法是實(shí)例方法,用于告訴此線(xiàn)程外部有中斷請求,并且將線(xiàn)程中的中斷標記設置為true
interrupted
此方法是類(lèi)方法,測試當前線(xiàn)程是否已經(jīng)中斷。線(xiàn)程的中斷狀態(tài) 由該方法清除。換句話(huà)說(shuō),如果連續兩次調用該方法,則第二次調用將返回 false(在***次調用已清除了其中斷狀態(tài)之后,且第二次調用檢驗完中斷狀態(tài)前,當前線(xiàn)程再次中斷的情況除外)。
isInterrupted
此方法是實(shí)例方法測試線(xiàn)程是否已經(jīng)中斷。線(xiàn)程的中斷狀態(tài) 不受該方法的影響。 線(xiàn)程中斷被忽略,因為在中斷時(shí)不處于活動(dòng)狀態(tài)的線(xiàn)程將由此返回 false 的方法反映出來(lái)
處理線(xiàn)程中斷的常用方法
設置取消標記
還是用上面的例子,只不過(guò)做了些修改
public static void main(String[] args) { NRunnable run=new NRunnable(); Thread t=new Thread(run); t.start(); System.out.println("is start......."); try { Thread.sleep(3000); } catch (InterruptedException e) { } run.cancel(); System.out.println("cancel ..."+System.currentTimeMillis()); } public static class NRunnable implements Runnable { public boolean isCancel=false; @Override public void run() { while(!isCancel) { System.out.println("我沒(méi)有種中斷"); try { Thread.sleep(10000); } catch (InterruptedException e) { } } System.out.println("我已經(jīng)結束了..."+System.currentTimeMillis()); } public void cancel() { this.isCancel=true; } }
執行結果如下:
is start.......
我沒(méi)有種中斷
cancel ...1438396915809
我已經(jīng)結束了...1438396922809
通過(guò)結果,我們發(fā)現線(xiàn)程確實(shí)已經(jīng)中斷了,但是細心的同學(xué)應該發(fā)現了一個(gè)問(wèn)題,調用cancel方法和***線(xiàn)程執行完畢之間隔了好幾秒的時(shí)間,也就是說(shuō)線(xiàn)程不是立馬中斷的,我們下面來(lái)分析一下原因:
子線(xiàn)程退出的條件是while循環(huán)結束,也就是cancel標示設置為true,但是當我們調用cancel方法將calcel標記設置為true 時(shí),while循環(huán)里面有一個(gè)耗時(shí)操作(sleep方法模擬),只有等待耗時(shí)操作執行完畢后才會(huì )去檢查這個(gè)標記,所以cancel方法和線(xiàn)程退出中間有時(shí) 間間隔。
通過(guò)interrupt
和 isinterrupt
方法來(lái)中斷線(xiàn)程
public static void main(String[] args) { Thread t=new NThread(); t.start(); System.out.println("is start......."); try { Thread.sleep(3000); } catch (InterruptedException e) { } System.out.println("start interrupt..."+System.currentTimeMillis()); t.interrupt(); System.out.println("end interrupt ..."+System.currentTimeMillis()); } public static class NThread extends Thread { @Override public void run() { while(!this.isInterrupted()) { System.out.println("我沒(méi)有種中斷"); try { Thread.sleep(10000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } System.out.println("我已經(jīng)結束了..."+System.currentTimeMillis()); } } }
運行結果如下:
is start.......
我沒(méi)有種中斷
start interrupt...1438398800110
我已經(jīng)結束了...1438398800110
end interrupt ...1438398800110
這次是立馬中斷的,但是這種方法是由局限性的,這種方法僅僅對于會(huì )拋出InterruptedException
異常的任務(wù)時(shí)有效的,比如java中的sleep、wait
等方法,對于不會(huì )拋出這種異常的任務(wù)其效果其實(shí)和***種方法是一樣的,都會(huì )有延遲性,這個(gè)例子中還有一個(gè)非常重要的地方就是cache語(yǔ)句中,我們調用了Thread.currentThread().interrupt()
我們把這句代碼去掉,運行你會(huì )發(fā)現這個(gè)線(xiàn)程無(wú)法終止,因為在拋出InterruptedException
的同時(shí),線(xiàn)程的中斷標志被清除了,所以在while語(yǔ)句中判斷當前線(xiàn)程是否中斷時(shí),返回的是false.針對InterruptedException
異常,我想說(shuō)的是:一定不能再catch語(yǔ)句塊中什么也不干,如果你實(shí)在不想處理,你可以將異常拋出來(lái),讓調用拋異常的方法也成為一個(gè)可以?huà)伋?code>InterruptedException 的方法,如果自己要捕獲此異常,那么***在cache語(yǔ)句中調用 Thread.currentThread().interrupt();
方法來(lái)讓高層只要中斷請求并處理該中斷。
對于上述兩種方法都有其局限性,***種方法只能處理那種工作量不大,會(huì )頻繁檢查循環(huán)標志的任務(wù),對于第二種方法適合用于拋出InterruptedException
的代碼。也就是說(shuō)***種和第二種方法支持的是支持中斷的線(xiàn)程任務(wù),那么不支持中斷的線(xiàn)程任務(wù)該怎么做呢。
例如 如果一個(gè)線(xiàn)程由于同步進(jìn)行I/O操作導致阻塞,中斷請求不會(huì )拋出InterruptedException
,我們該如何中斷此線(xiàn)程呢。
處理不支持中斷的線(xiàn)程中斷的常用方法
改寫(xiě)線(xiàn)程的interrupt方法
public static class ReaderThread extends Thread { public static final int BUFFER_SIZE=512; Socket socket; InputStream is; public ReaderThread(Socket socket) throws IOException { this.socket=socket; is=this.socket.getInputStream(); } @Override public void interrupt() { try { socket.close(); }catch(IOException e) { }finally { super.interrupt(); } super.interrupt(); } @Override public void run() { try { byte[]buf=new byte[BUFFER_SIZE]; while(true) { int count=is.read(buf); if(count<0) break; else if(count>0) { } } }catch(IOException e) { } } } }
例如在上面的例子中,改寫(xiě)了Thread的interrupt
方法,當調用interrupt
方法時(shí),會(huì )關(guān)閉socket,如果此時(shí)read方法阻塞,那么會(huì )拋出IOException
此時(shí)線(xiàn)程任務(wù)也就結束了。
以上方法是通過(guò)改寫(xiě)線(xiàn)程的interrupt
方法實(shí)現,那么對于使用線(xiàn)程池的任務(wù)該怎么中斷呢。
改寫(xiě)線(xiàn)程池的newTaskFor方法
通常我們向線(xiàn)程池中加入一個(gè)任務(wù)采用如下形式:
Future<?> future=executor.submit(new Runnable(){ @Override public void run() { } }); 取消任務(wù)時(shí),調用的是future的cancel方法,其實(shí)在cancel方法中調用的是線(xiàn)程的interrupt方法。所以對于不支持中斷的任務(wù)cancel也是無(wú)效的,下面我們看看submit方法里面干了上面吧 public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; } 這里調用的是AbstractExecutorService 的newTaskFor方法,那么我們能不能改寫(xiě)ThreadPoolExecutor的newTaskFor方法呢,接下來(lái)看我在處理吧 定義一個(gè)基類(lèi),所有需要取消的任務(wù)繼承這個(gè)基類(lèi) public interface CancelableRunnable<T> extends Runnable { public void cancel(); public RunnableFuture<T> newTask(); } 將上面的ReaderThread改為繼承這個(gè)類(lèi) public static class ReaderThread implements CancelableRunnable<Void> { public static final int BUFFER_SIZE=512; Socket socket; InputStream is; public ReaderThread(Socket socket) throws IOException { this.socket=socket; is=this.socket.getInputStream(); } @Override public void run() { try { byte[]buf=new byte[BUFFER_SIZE]; while(true) { int count=is.read(buf); if(count<0) break; else if(count>0) { } } }catch(IOException e) { } } @Override public void cancel() { try { socket.close(); } catch (IOException e) { } } @Override public RunnableFuture<Void> newTask() { return new FutureTask<Void>(this,null) { @Override public boolean cancel(boolean mayInterruptIfRunning) { return super.cancel(mayInterruptIfRunning); if(ReaderThread.this instanceof CancelableRunnable)) { ((CancelableRunnable)(ReaderThread.this)).cancel(); }else { super.cancel(mayInterruptIfRunning); } } }; } }
當你調用future的cancel的方法時(shí),它會(huì )關(guān)閉socket,最終導致read方法異常,從而終止線(xiàn)程任務(wù)。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站