设想这样的情景:我们的应用在某一个时间段内,需要一个子线程不停的在后台运行,这可能是一个下载过程,是一个对服务端socket的监听,也可能是一个绘图的计算过程。 当我们想要终止线程的时候,我们会怎样做呢?是设定一个标志变量来控制跳出循环?还是使用thread.stop()?又或者是设置thread = null? 有的时候我们需要一种规范的思路,使用规范的方法来解决一类问题。我们首先要明白,线程终止的条件,有三种情况: 1.当线程的run方法执行方法体中最后一条语句后。 2.当执行retutrn语句返回时。 3.当出现了在方法中没有捕获的异常时。 在Java的早期版本中,还有一个stop方法,其他线程可以调用它终止线程,但是这个方法已经被弃用了,所以还在用的同学就不要继续用了。 我们的正确思路是,使用interrupt方法来终止我们的线程。 首先要理解interrupt方法做了什么:每一个线程都有一个中断状态,这是一个boolean标志,当线程调用了interrupt方法时,这个中断状态就会被置位。如果我们要检查中断状态,可以使用Thread.currentThread()。isInterrupted()来获得是否中断。 但是如果线程被阻塞了(sleep or wait),当我们调用了interrupt方法的时候,就会产生InterruptedException异常。这是我们可以利用的地方。同样的,如果中断状态先被置位了,然后我们调用了sleep方法,线程不会休眠,相反,它将清除中断状态,然后抛出InterruptedException. 我们调用了interrupt并不意味着线程会终止,线程是否会终止,以及会如何继续,是程序员来控制的。 在本文中我们将会讨论终止线程的规范用法,然后在一个例子中实际应用,在这个例子中我们模拟了文件拷贝和游戏绘图两种情形。做出的效果如下图所示,点击start后上方进度条会显示文件拷贝的进度,点击end则会停止拷贝。点击draw会在画面中不停绘制各种各样的矩形,点击stop则会停止绘制。 首先我们来看两种情形的后台线程写法: public void run() { try { …… While(!Thread.currentThread.isInterrupted()&& more work to do) { do more work } }catch(InterruptedException){ //thread was interrupted duringsleep or wait } finally { cleanup, if required } //exiting the run method terminates thethread } ----------------------------------------------------------------------- public void run() { try{ …… while( more work to do){ do more work Thread.sleep(delay); } } catch(InterruptedException) { //thread was interrupted duringsleep or wait } finally{ cleanup, if required } //exitingthe run method terminates the thread } 第一种写法适用于后台下载,文件拷贝以及类似情形,第二种写法适合游戏画面刷新或者类似情形。 第一种写法利用了interrupt方法,作为终止的请求,使得循环跳出,run方法执行完毕。而第二种方法则是利用当线程sleep的时候调用interrupt会抛出InterruptedException从而跳出了循环进而线程执行到结束。 事实上这两种写法的区别就在于第二种使用了sleep. 在我们的使用示例中,对应这两种方法的使用代码如下: 这一段是实现文件拷贝的: private class CopyRunnable implements Runnable { @Override public void run() { File fromFile = new File(Environment.getExternalStorageDirectory() .getAbsolutePath() + "/abc.exe"); long fileLength = fromFile.length(); long copyedLength = 0; File toFile = new File(Environment.getExternalStorageDirectory() .getAbsolutePath() + "/abc_.exe"); if (toFile.exists()) { toFile.delete(); } try { FileInputStream fileInputStream = new FileInputStream(fromFile); FileOutputStream fileOutputStream = new FileOutputStream( toFile, true); byte[] buffer = new byte[2048]; int readLength = 0; while (!Thread.currentThread()。isInterrupted() && (readLength = fileInputStream.read(buffer)) != -1) { fileOutputStream.write(buffer, 0, buffer.length); copyedLength += readLength; int progress = (int) ((float) copyedLength / fileLength * 100); handler.obtainMessage(0, progress, 0)。sendToTarget(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { handler.obtainMessage(1)。sendToTarget(); } } } 这一段是实现矩形绘图的: private class DrawRunnable implementsRunnable { @Override public void run() { try { while (true) { long beginTime = System.currentTimeMillis(); paint.setColor(getColor()); getCoor(); postInvalidate(); long endTime = System.currentTimeMillis(); if (endTime - beginTime < 150) { Thread.sleep(150 - (endTime - beginTime)); } } } catch (InterruptedException e) { e.printStackTrace(); } finally { } } } |