分享

Java 基本功 之中断线程的理解

 天海544 2014-02-17

    设想这样的情景:我们的应用在某一个时间段内,需要一个子线程不停的在后台运行,这可能是一个下载过程,是一个对服务端socket的监听,也可能是一个绘图的计算过程。当我们想要终止线程的时候,我们会怎样做呢?是设定一个标志变量来控制跳出循环?还是使用thread.stop()?又或者是设置thread = null?

    有的时候我们需要一种规范的思路,使用规范的方法来解决一类问题。

    本文原创,如需转载,请注明转载地址:http://blog.csdn.net/carrey1989/article/details/11918303

    我们首先要明白,线程终止的条件,有三种情况:

    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 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

            Thread.sleep(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。

    在我们的使用示例中,对应这两种方法的使用代码如下:

    这一段是实现文件拷贝的:

  1. private class CopyRunnable implements Runnable {  
  2.   
  3.         @Override  
  4.         public void run() {  
  5.             File fromFile = new File(Environment.getExternalStorageDirectory()  
  6.                     .getAbsolutePath() + "/abc.exe");  
  7.             long fileLength = fromFile.length();  
  8.             long copyedLength = 0;  
  9.             File toFile = new File(Environment.getExternalStorageDirectory()  
  10.                     .getAbsolutePath() + "/abc_.exe");  
  11.             if (toFile.exists()) {  
  12.                 toFile.delete();  
  13.             }  
  14.             try {  
  15.                 FileInputStream fileInputStream = new FileInputStream(fromFile);  
  16.                 FileOutputStream fileOutputStream = new FileOutputStream(  
  17.                         toFile, true);  
  18.                 byte[] buffer = new byte[2048];  
  19.                 int readLength = 0;  
  20.                 while (!Thread.currentThread().isInterrupted()  
  21.                         && (readLength = fileInputStream.read(buffer)) != -1) {  
  22.                     fileOutputStream.write(buffer, 0, buffer.length);  
  23.                     copyedLength += readLength;  
  24.                     int progress = (int) ((float) copyedLength / fileLength * 100);  
  25.                     handler.obtainMessage(0, progress, 0).sendToTarget();  
  26.                 }  
  27.             } catch (FileNotFoundException e) {  
  28.                 e.printStackTrace();  
  29.             } catch (IOException e) {  
  30.                 e.printStackTrace();  
  31.             } finally {  
  32.                 handler.obtainMessage(1).sendToTarget();  
  33.             }  
  34.         }  
  35.   
  36.     }  
    这一段是实现矩形绘图的:
  1. private class DrawRunnable implements Runnable {  
  2.   
  3.         @Override  
  4.         public void run() {  
  5.             try {  
  6.                 while (true) {  
  7.                     long beginTime = System.currentTimeMillis();  
  8.                     paint.setColor(getColor());  
  9.                     getCoor();  
  10.                     postInvalidate();  
  11.                     long endTime = System.currentTimeMillis();  
  12.                     if (endTime - beginTime < 150) {  
  13.                         Thread.sleep(150 - (endTime - beginTime));  
  14.                     }  
  15.                 }  
  16.             } catch (InterruptedException e) {  
  17.                 e.printStackTrace();  
  18.             } finally {  
  19.   
  20.             }  
  21.         }  
  22.   
  23.     }  

    实际上这两种写法都是利用了interrupt方法的特点,通过线程的中断置位或者异常抛出来跳出循环进而终结线程。如果对这段代码感兴趣,可以到文章最后下载代码。

    最后做一下方法总结:


    void interrupt()

    向线程发送中断请求。线程的中断状态将被设置为true。如果目前该线程被一个sleep调用阻塞,那么,InterruptedException异常被抛出。


    static boolean interrupted()

    测试当前线程(即正在执行这一命令的线程)是否被中断,注意,这是一个静态方法。这一调用会产生副作用,它将当前线程的中断状态设置为false。


    boolean isInterrupted()

    测试线程是否被中断。不像静态的中断方法,这一调用不会改变线程的中断状态。


    static Thread currentThread()

    返回代表当前执行线程的Thread对象。


   源码下载

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多