java的中断机制_深入分析Java线程中断机制

Thread.interrupt真的能中断线程吗

在平时的开发过程中,相信都会使用到多线程,在使用多线程时,大家也会遇到各种各样的问题,今天我们就来说说一个多线程的问题——线程中断。在 java中启动线程非常容易,大多数情况下我是让一个线程执行完自己的任务然后自己停掉,但是有时候我们需要取消某个操作,比如你在网络下载时,有时候需 要取消下载。实现线程的安全中断并不是一件容易的事情,因为Java并不支持安全快速中断线程的机制,这里估计很多同学就会说了,java不是提供了Thread.interrupt 方法中断线程吗,好吧,我们今天就从这个方法开始说起。

26b579b0107ed0fc57d83c3bb23008ad.png

但是调用此方法线程真的会停止吗?我们写个demo看看就知道了。

publicclassMain {

privatestaticfinalString TAG ="Main";

publicstaticvoidmain(String[] args) {

Thread t=newThread(newNRunnable());

t.start();

System.out.println("is start.......");

try{

Thread.sleep(3000);

} catch(InterruptedException e) {

}

t.interrupt();

System.out.println("is interrupt.......");

}

publicstaticclassNRunnableimplementsRunnable

{

@Override

publicvoidrun() {

while(true)

{

System.out.println("我没有种中断");

try{

Thread.sleep(1000);

} catch(InterruptedException e) {

}

}

}

}

}

如果interrutp方法能够中断线程,那么在打印了is interrupt…….之后应该是没有log了,我们看看执行结果吧

is start.......

我没有种中断

我没有种中断

我没有种中断

我没有种中断

我没有种中断

is interrupt.......

我没有种中断

我没有种中断

我没有种中断

我没有种中断

我没有种中断

....

通过结果可以发现子线程并没有中断

所以 Thread.interrupt() 方法并不能中断线程,该方法仅仅告诉线程外部已经有中断请求,至于是否中断还取决于线程自己。在Thread类中除了interrupt() 方法还有另外两个非常相似的方法:interrupted 和 isInterrupted 方法,下面来对这几个方法进行说明:

interrupt 此方法是实例方法,用于告诉此线程外部有中断请求,并且将线程中的中断标记设置为true

interrupted 此方法是类方法,测试当前线程是否已经中断。线程的中断状态 由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在***次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。

isInterrupted 此方法是实例方法测试线程是否已经中断。线程的中断状态 不受该方法的影响。 线程中断被忽略,因为在中断时不处于活动状态的线程将由此返回 false 的方法反映出来

处理线程中断的常用方法

设置取消标记

还是用上面的例子,只不过做了些修改

publicstaticvoidmain(String[] args) {

NRunnable run=newNRunnable();

Thread t=newThread(run);

t.start();

System.out.println("is start.......");

try{

Thread.sleep(3000);

} catch(InterruptedException e) {

}

run.cancel();

System.out.println("cancel ..."+System.currentTimeMillis());

}

publicstaticclassNRunnableimplementsRunnable

{

publicbooleanisCancel=false;

@Override

publicvoidrun() {

while(!isCancel)

{

System.out.println("我没有种中断");

try{

Thread.sleep(10000);

} catch(InterruptedException e) {

}

}

System.out.println("我已经结束了..."+System.currentTimeMillis());

}

publicvoidcancel()

{

this.isCancel=true;

}

}

执行结果如下:

is start.......

我没有种中断

cancel ...1438396915809

我已经结束了...1438396922809

通过结果,我们发现线程确实已经中断了,但是细心的同学应该发现了一个问题,调用cancel方法和***线程执行完毕之间隔了好几秒的时间,也就是说线程不是立马中断的,我们下面来分析一下原因:

子线程退出的条件是while循环结束,也就是cancel标示设置为true,但是当我们调用cancel方法将calcel标记设置为true 时,while循环里面有一个耗时操作(sleep方法模拟),只有等待耗时操作执行完毕后才会去检查这个标记,所以cancel方法和线程退出中间有时 间间隔。

通过interrupt 和 isinterrupt 方法来中断线程

publicstaticvoidmain(String[] args) {

Thread t=newNThread();

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());

}

publicstaticclassNThreadextendsThread

{

@Override

publicvoidrun() {

while(!this.isInterrupted())

{

System.out.println("我没有种中断");

try{

Thread.sleep(10000);

} catch(InterruptedException e) {

Thread.currentThread().interrupt();

}

}

System.out.println("我已经结束了..."+System.currentTimeMillis());

}

}

}

运行结果如下:

is start.......

我没有种中断

start interrupt...1438398800110

我已经结束了...1438398800110

end interrupt ...1438398800110

这次是立马中断的,但是这种方法是由局限性的,这种方法仅仅对于会抛出InterruptedException 异常的任务时有效的,比如java中的sleep、wait 等方法,对于不会抛出这种异常的任务其效果其实和***种方法是一样的,都会有延迟性,这个例子中还有一个非常重要的地方就是cache语句中,我们调用了Thread.currentThread().interrupt() 我们把这句代码去掉,运行你会发现这个线程无法终止,因为在抛出InterruptedException 的同时,线程的中断标志被清除了,所以在while语句中判断当前线程是否中断时,返回的是false.针对InterruptedException 异常,我想说的是:一定不能再catch语句块中什么也不干,如果你实在不想处理,你可以将异常抛出来,让调用抛异常的方法也成为一个可以抛出InterruptedException 的方法,如果自己要捕获此异常,那么***在cache语句中调用 Thread.currentThread().interrupt(); 方法来让高层只要中断请求并处理该中断。

对于上述两种方法都有其局限性,***种方法只能处理那种工作量不大,会频繁检查循环标志的任务,对于第二种方法适合用于抛出InterruptedException的代码。也就是说***种和第二种方法支持的是支持中断的线程任务,那么不支持中断的线程任务该怎么做呢。

例如 如果一个线程由于同步进行I/O操作导致阻塞,中断请求不会抛出InterruptedException ,我们该如何中断此线程呢。

处理不支持中断的线程中断的常用方法

改写线程的interrupt方法

publicstaticclassReaderThreadextendsThread

{

publicstaticfinalintBUFFER_SIZE=512;

Socket socket;

InputStream is;

publicReaderThread(Socket socket)throwsIOException

{

this.socket=socket;

is=this.socket.getInputStream();

}

@Override

publicvoidinterrupt() {

try

{

socket.close();

}catch(IOException e)

{

}finally

{

super.interrupt();

}

super.interrupt();

}

@Override

publicvoidrun() {

try

{

byte[]buf=newbyte[BUFFER_SIZE];

while(true)

{

intcount=is.read(buf);

if(count<0)

break;

elseif(count>0)

{

}

}

}catch(IOException e)

{

}

}

}

}

例如在上面的例子中,改写了Thread的interrupt 方法,当调用interrupt 方法时,会关闭socket,如果此时read方法阻塞,那么会抛出IOException 此时线程任务也就结束了。

以上方法是通过改写线程的interrupt 方法实现,那么对于使用线程池的任务该怎么中断呢。

改写线程池的newTaskFor方法

通常我们向线程池中加入一个任务采用如下形式:

Future> future=executor.submit(newRunnable(){

@Override

publicvoidrun() {

}

});

取消任务时,调用的是future的cancel方法,其实在cancel方法中调用的是线程的interrupt方法。所以对于不支持中断的任务cancel也是无效的,下面我们看看submit方法里面干了上面吧

publicFuture> submit(Runnable task) {

if(task ==null)thrownewNullPointerException();

RunnableFuture ftask = newTaskFor(task, null);

execute(ftask);

returnftask;

}

这里调用的是AbstractExecutorService 的newTaskFor方法,那么我们能不能改写ThreadPoolExecutor的newTaskFor方法呢,接下来看我在处理吧

定义一个基类,所有需要取消的任务继承这个基类

publicinterfaceCancelableRunnableextendsRunnable {

publicvoidcancel();

publicRunnableFuture newTask();

}

将上面的ReaderThread改为继承这个类

publicstaticclassReaderThreadimplementsCancelableRunnable

{

publicstaticfinalintBUFFER_SIZE=512;

Socket socket;

InputStream is;

publicReaderThread(Socket socket)throwsIOException

{

this.socket=socket;

is=this.socket.getInputStream();

}

@Override

publicvoidrun() {

try

{

byte[]buf=newbyte[BUFFER_SIZE];

while(true)

{

intcount=is.read(buf);

if(count<0)

break;

elseif(count>0)

{

}

}

}catch(IOException e)

{

}

}

@Override

publicvoidcancel() {

try{

socket.close();

} catch(IOException e) {

}

}

@Override

publicRunnableFuture newTask() {

returnnewFutureTask(this,null)

{

@Override

publicbooleancancel(booleanmayInterruptIfRunning) {

returnsuper.cancel(mayInterruptIfRunning);

if(ReaderThread.thisinstanceofCancelableRunnable))

{

((CancelableRunnable)(ReaderThread.this)).cancel();

}else

{

super.cancel(mayInterruptIfRunning);

}

}

};

}

}

当你调用future的cancel的方法时,它会关闭socket,最终导致read方法异常,从而终止线程任务。

【编辑推荐】

【责任编辑:wangxueyan TEL:(010)68476606】

点赞 0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值