本篇文章為大家展示了Java中線程中斷機(jī)制的原理是什么,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。
成都創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供鲅魚圈網(wǎng)站建設(shè)、鲅魚圈做網(wǎng)站、鲅魚圈網(wǎng)站設(shè)計(jì)、鲅魚圈網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、鲅魚圈企業(yè)網(wǎng)站模板建站服務(wù),十年鲅魚圈做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
Thread.interrupt真的能中斷線程嗎
在平時(shí)的開發(fā)過(guò)程中,相信都會(huì)使用到多線程,在使用多線程時(shí),大家也會(huì)遇到各種各樣的問(wèn)題,今天我們就來(lái)說(shuō)說(shuō)一個(gè)多線程的問(wèn)題——線程中斷。在 java中啟動(dòng)線程非常容易,大多數(shù)情況下我是讓一個(gè)線程執(zhí)行完自己的任務(wù)然后自己停掉,但是有時(shí)候我們需要取消某個(gè)操作,比如你在網(wǎng)絡(luò)下載時(shí),有時(shí)候需 要取消下載。實(shí)現(xiàn)線程的安全中斷并不是一件容易的事情,因?yàn)镴ava并不支持安全快速中斷線程的機(jī)制,這里估計(jì)很多同學(xué)就會(huì)說(shuō)了,java不是提供了Thread.interrupt
方法中斷線程嗎,好吧,我們今天就從這個(gè)方法開始說(shuō)起。
但是調(diào)用此方法線程真的會(huì)停止嗎?我們寫個(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方法能夠中斷線程,那么在打印了is interrupt…….之后應(yīng)該是沒(méi)有l(wèi)og了,我們看看執(zhí)行結(jié)果吧
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ò)結(jié)果可以發(fā)現(xiàn)子線程并沒(méi)有中斷
所以 Thread.interrupt()
方法并不能中斷線程,該方法僅僅告訴線程外部已經(jīng)有中斷請(qǐng)求,至于是否中斷還取決于線程自己。在Thread類中除了interrupt()
方法還有另外兩個(gè)非常相似的方法:interrupted
和 isInterrupted
方法,下面來(lái)對(duì)這幾個(gè)方法進(jìn)行說(shuō)明:
interrupt
此方法是實(shí)例方法,用于告訴此線程外部有中斷請(qǐng)求,并且將線程中的中斷標(biāo)記設(shè)置為true
interrupted
此方法是類方法,測(cè)試當(dāng)前線程是否已經(jīng)中斷。線程的中斷狀態(tài) 由該方法清除。換句話說(shuō),如果連續(xù)兩次調(diào)用該方法,則第二次調(diào)用將返回 false(在***次調(diào)用已清除了其中斷狀態(tài)之后,且第二次調(diào)用檢驗(yàn)完中斷狀態(tài)前,當(dāng)前線程再次中斷的情況除外)。
isInterrupted
此方法是實(shí)例方法測(cè)試線程是否已經(jīng)中斷。線程的中斷狀態(tài) 不受該方法的影響。 線程中斷被忽略,因?yàn)樵谥袛鄷r(shí)不處于活動(dòng)狀態(tài)的線程將由此返回 false 的方法反映出來(lái)
處理線程中斷的常用方法
設(shè)置取消標(biāo)記
還是用上面的例子,只不過(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)結(jié)束了..."+System.currentTimeMillis()); } public void cancel() { this.isCancel=true; } }
執(zhí)行結(jié)果如下:
is start.......
我沒(méi)有種中斷
cancel ...1438396915809
我已經(jīng)結(jié)束了...1438396922809
通過(guò)結(jié)果,我們發(fā)現(xiàn)線程確實(shí)已經(jīng)中斷了,但是細(xì)心的同學(xué)應(yīng)該發(fā)現(xiàn)了一個(gè)問(wèn)題,調(diào)用cancel方法和***線程執(zhí)行完畢之間隔了好幾秒的時(shí)間,也就是說(shuō)線程不是立馬中斷的,我們下面來(lái)分析一下原因:
子線程退出的條件是while循環(huán)結(jié)束,也就是cancel標(biāo)示設(shè)置為true,但是當(dāng)我們調(diào)用cancel方法將calcel標(biāo)記設(shè)置為true 時(shí),while循環(huán)里面有一個(gè)耗時(shí)操作(sleep方法模擬),只有等待耗時(shí)操作執(zhí)行完畢后才會(huì)去檢查這個(gè)標(biāo)記,所以cancel方法和線程退出中間有時(shí) 間間隔。
通過(guò)interrupt
和 isinterrupt
方法來(lái)中斷線程
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)結(jié)束了..."+System.currentTimeMillis()); } } }
運(yùn)行結(jié)果如下:
is start.......
我沒(méi)有種中斷
start interrupt...1438398800110
我已經(jīng)結(jié)束了...1438398800110
end interrupt ...1438398800110
這次是立馬中斷的,但是這種方法是由局限性的,這種方法僅僅對(duì)于會(huì)拋出InterruptedException
異常的任務(wù)時(shí)有效的,比如java中的sleep、wait
等方法,對(duì)于不會(huì)拋出這種異常的任務(wù)其效果其實(shí)和***種方法是一樣的,都會(huì)有延遲性,這個(gè)例子中還有一個(gè)非常重要的地方就是cache語(yǔ)句中,我們調(diào)用了Thread.currentThread().interrupt()
我們把這句代碼去掉,運(yùn)行你會(huì)發(fā)現(xiàn)這個(gè)線程無(wú)法終止,因?yàn)樵趻伋?code>InterruptedException 的同時(shí),線程的中斷標(biāo)志被清除了,所以在while語(yǔ)句中判斷當(dāng)前線程是否中斷時(shí),返回的是false.針對(duì)InterruptedException
異常,我想說(shuō)的是:一定不能再catch語(yǔ)句塊中什么也不干,如果你實(shí)在不想處理,你可以將異常拋出來(lái),讓調(diào)用拋異常的方法也成為一個(gè)可以拋出InterruptedException
的方法,如果自己要捕獲此異常,那么***在cache語(yǔ)句中調(diào)用 Thread.currentThread().interrupt();
方法來(lái)讓高層只要中斷請(qǐng)求并處理該中斷。
對(duì)于上述兩種方法都有其局限性,***種方法只能處理那種工作量不大,會(huì)頻繁檢查循環(huán)標(biāo)志的任務(wù),對(duì)于第二種方法適合用于拋出InterruptedException
的代碼。也就是說(shuō)***種和第二種方法支持的是支持中斷的線程任務(wù),那么不支持中斷的線程任務(wù)該怎么做呢。
例如 如果一個(gè)線程由于同步進(jìn)行I/O操作導(dǎo)致阻塞,中斷請(qǐng)求不會(huì)拋出InterruptedException
,我們?cè)撊绾沃袛啻司€程呢。
處理不支持中斷的線程中斷的常用方法
改寫線程的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) { } } } }
例如在上面的例子中,改寫了Thread的interrupt
方法,當(dāng)調(diào)用interrupt
方法時(shí),會(huì)關(guān)閉socket,如果此時(shí)read方法阻塞,那么會(huì)拋出IOException
此時(shí)線程任務(wù)也就結(jié)束了。
以上方法是通過(guò)改寫線程的interrupt
方法實(shí)現(xiàn),那么對(duì)于使用線程池的任務(wù)該怎么中斷呢。
改寫線程池的newTaskFor方法
通常我們向線程池中加入一個(gè)任務(wù)采用如下形式:
Future<?> future=executor.submit(new Runnable(){ @Override public void run() { } }); 取消任務(wù)時(shí),調(diào)用的是future的cancel方法,其實(shí)在cancel方法中調(diào)用的是線程的interrupt方法。所以對(duì)于不支持中斷的任務(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; } 這里調(diào)用的是AbstractExecutorService 的newTaskFor方法,那么我們能不能改寫ThreadPoolExecutor的newTaskFor方法呢,接下來(lái)看我在處理吧 定義一個(gè)基類,所有需要取消的任務(wù)繼承這個(gè)基類 public interface CancelableRunnable<T> extends Runnable { public void cancel(); public RunnableFuture<T> newTask(); } 將上面的ReaderThread改為繼承這個(gè)類 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); } } }; } }
當(dāng)你調(diào)用future的cancel的方法時(shí),它會(huì)關(guān)閉socket,最終導(dǎo)致read方法異常,從而終止線程任務(wù)。
上述內(nèi)容就是Java中線程中斷機(jī)制的原理是什么,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
網(wǎng)站欄目:Java中線程中斷機(jī)制的原理是什么
文章位置:http://chinadenli.net/article36/gesspg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信公眾號(hào)、、移動(dòng)網(wǎng)站建設(shè)、營(yíng)銷型網(wǎng)站建設(shè)、域名注冊(cè)、網(wǎng)站設(shè)計(jì)公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)