设想这样的情景我们的应用在某一个时间段内需要一个子线程不停的在后台运行这可能是一个下载过程是一个对服务端socket的监听也可能是一个绘图的计算过程当我们想要终止线程的时候我们会怎样做呢?是设定一个标志变量来控制跳出循环?还是使用threadstop()?又或者是设置thread = null?
有的时候我们需要一种规范的思路使用规范的方法来解决一类问题
我们首先要明白线程终止的条件有三种情况
当线程的run方法执行方法体中最后一条语句后
当执行retutrn语句返回时
当出现了在方法中没有捕获的异常时
在Java的早期版本中还有一个stop方法其他线程可以调用它终止线程但是这个方法已经被弃用了所以还在用的同学就不要继续用了
我们的正确思路是使用interrupt方法来终止我们的线程
首先要理解interrupt方法做了什么每一个线程都有一个中断状态这是一个boolean标志当线程调用了interrupt方法时这个中断状态就会被置位如果我们要检查中断状态可以使用ThreadcurrentThread()isInterrupted()来获得是否中断
但是如果线程被阻塞了(sleep or wait)当我们调用了interrupt方法的时候就会产生InterruptedException异常这是我们可以利用的地方
同样的如果中断状态先被置位了然后我们调用了sleep方法线程不会休眠相反它将清除中断状态然后抛出InterruptedException
我们调用了interrupt并不意味着线程会终止线程是否会终止以及会如何继续是程序员来控制的
在本文中我们将会讨论终止线程的规范用法然后在一个例子中实际应用在这个例子中我们模拟了文件拷贝和游戏绘图两种情形做出的效果如下图所示点击start后上方进度条会显示文件拷贝的进度点击end则会停止拷贝点击draw会在画面中不停绘制各种各样的矩形点击stop则会停止绘制
首先我们来看两种情形的后台线程写法
public void run() {
try{
…
while(!ThreadcurrentThreadisInterrupted() && more work to do)
{
do more work
}
}
catch(InterruptedException)
{
//thread was interrupted during sleep or wait
}
finally
{
cleanup if required
}
//exiting the run method terminates the thread }
public void run() {
try{
…
while( more work to do)
{
do more work
Threadsleep(delay)
}
}
catch(InterruptedException)
{
//thread was interrupted during sleep or wait
}
finally
{
cleanup if required
}
//exiting the run method terminates the thread }
第一种写法适用于后台下载文件拷贝以及类似情形第二种写法适合游戏画面刷新或者类似情形
第一种写法利用了interrupt方法作为终止的请求使得循环跳出run方法执行完毕而第二种方法则是利用当线程sleep的时候调用interrupt会抛出InterruptedException从而跳出了循环进而线程执行到结束
事实上这两种写法的区别就在于第二种使用了sleep
在我们的使用示例中对应这两种方法的使用代码如下
这一段是实现文件拷贝的
private class CopyRunnable implements Runnable {
@Override
public void run() {
File fromFile = new File(EnvironmentgetExternalStorageDirectory()
getAbsolutePath() + /abcexe)
long fileLength = fromFilelength()
long copyedLength = ;
File toFile = new File(EnvironmentgetExternalStorageDirectory()
getAbsolutePath() + /abc_exe)
if (toFileexists()) {
toFiledelete()
}
try {
FileInputStream fileInputStream = new FileInputStream(fromFile)
FileOutputStream fileOutputStream = new FileOutputStream(
toFile true)
byte[] buffer = new byte[];
int readLength = ;
while (!ThreadcurrentThread()isInterrupted()
&& (readLength = fileInputStreamread(buffer)) != ) {
fileOutputStreamwrite(buffer bufferlength)
copyedLength += readLength;
int progress = (int)
((float) copyedLength / fileLength * )
handlerobtainMessage( progress )sendToTarget()
}
} catch (FileNotFoundException e) {
eprintStackTrace()
} catch (IOException e) {
eprintStackTrace()
} finally {
handlerobtainMessage()sendToTarget()
}
}
}
这一段是实现矩形绘图的
private class DrawRunnable implements Runnable {
@Override
public void run() {
try {
while (true) {
long beginTime = SystemcurrentTimeMillis()
paintsetColor(getColor())
getCoor()
postInvalidate()
long endTime = SystemcurrentTimeMillis()
if (endTime beginTime < ) {
Threadsleep( (endTime beginTime))
}
}
} catch (InterruptedException e) {
eprintStackTrace()
} finally {
}
}
}
实际上这两种写法都是利用了interrupt方法的特点通过线程的中断置位或者异常抛出来跳出循环进而终结线程如果对这段代码感兴趣可以到文章最后下载代码
最后做一下方法总结
void interrupt()
向线程发送中断请求线程的中断状态将被设置为true如果目前该线程被一个sleep调用阻塞那么InterruptedException异常被抛出
static boolean interrupted()
测试当前线程(即正在执行这一命令的线程)是否被中断注意这是一个静态方法这一调用会产生副作用它将当前线程的中断状态设置为false
boolean isInterrupted()
测试线程是否被中断不像静态的中断方法这一调用不会改变线程的中断状态
static Thread currentThread()
返回代表当前执行线程的Thread对象