最近在做项目,遇到一个问题:如果停止一个Java线程。
Java推荐的标准方法:使用interrupt终止线程
如何使用interrupt中断一个线程?通常的做法是在线程外部调用interrupt方法,线程内部会接收到相应异常,然后在异常处理中安全退出线程。请看下面的例子:
public class MyThread implements Runnable
{
private Thread runner = new Thread(this, "runner");
public void start()
{
runner.start();
}
public void interrupt()
{
runner.interrupt();
}
@Override
public void run()
{
while(true)
{
try
{
Thread.sleep(1000);
System.out.println("thread is running.");
}
catch(Exception e)
{
System.out.println("thread exit.");
break;
}
}
}
public static void main(String[] args)
{
MyThread myThread = new MyThread();
try
{
myThread.start();
Thread.sleep(5000);
myThread.interrupt();
}
catch(Exception e)
{
System.out.println(e);
}
}
}
那么,是否调用interrupt方法,线程内部都会收到响应异常?其实不是这样,如果线程内部没有调用sleep/wait/join等方法,线程是不会接收到响应异常的。请看看下面的例子。
public class MyThread implements Runnable
{
private Thread runner = new Thread(this, "runner");
public void start()
{
runner.start();
}
public void interrupt()
{
runner.interrupt();
}
@Override
public void run()
{
try
{
while(true)
{
int a = 1;
}
}
catch(Exception e)
{
System.out.println(e);
}
}
public static void main(String[] args)
{
MyThread myThread = new MyThread();
try
{
myThread.start();
Thread.sleep(5000);
myThread.interrupt();
}
catch(Exception e)
{
System.out.println(e);
}
}
}
参考JDK文档可知,调用interrupt终止线程可分为以下几种情况:
a> 如果线程被阻塞在wait/sleep/join等方法调用,调用interrupt方法,线程内部会接受到InterruptedException
b> 如果线程被阻塞在基于interruptible channel实现的I/O操作,调用interrrupt方法,通道会被关闭,线程内部会接收到ClosedByInterruptException.
c> 如果线程被阻塞在Selector,selection操作会立刻返回,且返回值非负。
d> 除了上述情况,其他情况Interrupt只会简单是设置中断标识位。
从上面描述可知,这里需要解决两个问题:
对于没有阻塞的方法,使用interrupt如何终止线程;
对于没有基于interruptible channel实现的I/O操作,如果终止线程。
看第一个问题:终止没有阻塞的线程。
对于这类线程,调用interrupt,线程内部不会受到相应异常,只是设置了中断标识,这时候可以通过检测中断标识位,判断是否终止线程。
public class MyThread implements Runnable
{
private Thread runner = new Thread(this, "runner");
public void start()
{
runner.start();
}
public void interrupt()
{
runner.interrupt();
}
@Override
public void run()
{
while(true)
{
if(runner.isInterrupted())
{
System.out.println("thread exit.");
break;
}
}
}
public static void main(String[] args)
{
MyThread myThread = new MyThread();
try
{
myThread.start();
Thread.sleep(5000);
myThread.interrupt();
}
catch(Exception e)
{
System.out.println(e);
}
}
}
第二个问题:终止没有基于interruptible channel实现的I/O操作。这时候Interrupt方法就无能为力,可以尝试使用Thread.stop,关于stop的使用下面详细讨论
使用stop方法终止线程
使用stop终止线程,线程会被立刻终止掉。如下面代码所示
public class MyThread implements Runnable
{
private Thread runner = new Thread(this, "runner");
public void start()
{
runner.start();
}
public void stop()
{
runner.stop();
}
public void interrupt()
{
runner.interrupt();
}
@Override
public void run()
{
try
{
while(true)
{
int a = 1;
}
}
catch(Throwable e)
{
System.out.println(e);
}
}
public static void main(String[] args)
{
MyThread myThread = new MyThread();
try
{
myThread.start();
Thread.sleep(5000);
myThread.stop();
}
catch(Exception e)
{
System.out.println(e);
}
}
}
Stop方法现在已经不推荐使用了。详见Java的官方文档,对于不建议使用stop等方法的说明:
http://docs.oracle.com/javase/1.5.0/docs/guide/misc/threadPrimitiveDeprecation.html
主要原因是,使用stop后,线程会立即是否所持有的锁。使用锁的本意通常是为了同步,保证数据的一致性。如果从A账户向B账户转账100元,如果在中间释放了锁,其他线程继续读取数据,很可能出现数据部一致。
public synchronized void ProcessData(int data)
{
accountA = accountA - data;
accountB = accountB + data;
}
另外,使用stop,线程会受到ThreadDeath的异常,而且这个异常可能发生在任何时候,包括catch和finally。
那么使用stop是否可以终止任何线程?其实不是这样,如下面代码所示:
public class MyThread extends Thread
{
@Override
public synchronized void run()
{
try
{
while(true)
{
int a = 1;
}
}
catch(Throwable e)
{
System.out.println(e);
}
}
public static void main(String[] args)
{
MyThread myThread = new MyThread();
try
{
myThread.start();
Thread.sleep(5000);
myThread.stop();
}
catch(Exception e)
{
System.out.println(e);
}
}
}
查看Thread.stop的源码可以发现,stop是一个同步方法,需要获取到一个同步锁,以为要线程run方法已占用的了锁,且不释放,所以stop方法一样也无法终止。附Thread.stop源码:
/**
* @deprecated Method stop is deprecated
*/
public final synchronized void stop(Throwable throwable)
{
if(throwable == null)
throw new NullPointerException();
SecurityManager securitymanager = System.getSecurityManager();
if(securitymanager != null)
{
checkAccess();
if(this != currentThread() || !(throwable instanceof ThreadDeath))
securitymanager.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
}
if(threadStatus != 0)
resume();
stop0(throwable);
}
终止I/O读写的线程:待补充
终止线程池中的线程:待补充
线程创建于任务执行的分离:待补充
参考资料:
http://forward.com.au/javaProgramming/HowToStopAThread.html
http://kdisk-sina-com.iteye.com/blog/835890
http://www.javacoffeebreak.com/articles/network_timeouts/