2025年3月29日 星期六 甲辰(龙)年 月廿八 夜 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > 安卓(android)开发

android 线程中断

时间:02-05来源:作者:点击数:41

项目代码中使用了线程的中断函数,和判断线程是否中断了停止while循环,项目使用一直也什么问题,突然间,我就想证实一下,这样是否有问题,代码如下:

  • class MyThread : Thread() {
  • override fun run() {
  • Log.i("ABCD", "11111")
  • try {
  • sleep(10_000)
  • } catch (e: Exception) {
  • Log.i("ABCD", "发生异常", e)
  • }
  • Log.i("ABCD", "22222")
  • }
  • }
  • private var thread: MyThread? = null
  • fun startThread(view: View) {
  • if (thread == null) {
  • thread = MyThread().apply { start() }
  • }
  • }
  • fun interruptThread(view: View) {
  • thread?.interrupt()
  • thread {
  • while (thread?.isInterrupted == false) {
  • Log.i("ABCD", "线程还没被中断")
  • Thread.sleep(1000)
  • }
  • Log.i("ABCD", "线程已被中断")
  • thread = null
  • }
  • }

startThread和interruptThread分别对应两个按钮的点击事件,当我点击开启线程按钮时线程开始启动,然后我立马按下中断线程按钮,输出的Log如下:

  • 2020-09-25 10:43:04.978 14718-15131/com.evendai.demo I/ABCD: 11111
  • 2020-09-25 10:43:07.187 14718-15131/com.evendai.demo I/ABCD: 发生异常
  • java.lang.InterruptedException
  • at java.lang.Thread.sleep(Native Method)
  • at java.lang.Thread.sleep(Thread.java:443)
  • at java.lang.Thread.sleep(Thread.java:359)
  • at com.evendai.demo.MainActivity$MyThread.run(MainActivity.kt:41)
  • 2020-09-25 10:43:07.187 14718-15131/com.evendai.demo I/ABCD: 22222
  • 2020-09-25 10:43:07.187 14718-15132/com.evendai.demo I/ABCD: 线程还没被中断
  • 2020-09-25 10:43:08.188 14718-15132/com.evendai.demo I/ABCD: 线程还没被中断
  • 2020-09-25 10:43:09.189 14718-15132/com.evendai.demo I/ABCD: 线程还没被中断

可以看到,在调用线程中断函数时,sleep()函数会立马抛出异常,我们进行了try catch,这时我们看到后面的“2222"立马就输出了,然后另一个线程检测此线程的中断状态时,一直是未中断的状态,所以,实验证明,即使调用thread.interrupt()函数,线程run方法里的代码可能还是在执行的,interupt()函数无法阻断run函数中代码的执行。

实验到这里,心里一惊,项目中一直是这么用的,好像也没什么问题,可能出了问题我也不知道,比如有时候卡死了也不知道是这不是这个导致的,我项目中run方法里面写了一个while循环,通过线程的isInterrupted状态来决定何时退出循环,一直没出问题可能是因为我把整个while做了一个try catch,当调用interupt()时,run里面抛出的中断异常被捕捉了,所以没什么问题,但现在,起码我知道了,可能run里面的一些代码在调用了interupt()后还是在执行的。

OK,到了这里总结一下,一般我们在线程写while循环时,条件要使用一个自定义的boolean变量来控制,当不需要循环时,把该变量设置为false。不要使用interrupt()函数和isInterrupted状态来控制while循环。赶紧把项目的实现改一改。

后续:代码还是不能乱改,要搞搞清楚,我项目中while循环里面会从队列里面读数据,简化的代码如下:

  • while (needRun) {
  • val byteArray = mPCMDataQueue.take()
  • }

take()函数的功能就是等待队列里的数据,如果没有数据就一直等着,这会阻塞线程的,所以如果我在想退出循环的地方,只是简单的把needRun设置为false,这是有问题的,因为代码走不到while(needRun)这里了,代码一直在mPCMDataQueue.take()这里等着数据的输入的呢?所以还是得调用thread.interrupt(),它会让take()函数抛出异常,这样我们的代码才能继续往下走。所以interrupt()函数还是有用的,同时while也是需要使用自己的boolean变量来控制,比如,因为上面我们试验也知道了,调用interrupt()函数后isInterrupted状态还可能是false的,示例代码如下:

  • class MyThread : Thread() {
  • private var queue: BlockingQueue<ByteArray> = LinkedBlockingQueue(5)
  • override fun run() {
  • while (!isInterrupted) {
  • Log.i("ABCD", "11111")
  • try {
  • val data = queue.take()
  • } catch (e: Exception) {
  • Log.i("ABCD", "发生异常", e)
  • }
  • Log.i("ABCD", "22222")
  • }
  • }
  • }

当代码执行到queue.take()处时,我们调用interrupt()函数,此时会抛出异常,由于我们做了try catch,此时cpu会从catch的地方继承往下执行,然后也会执行到while(!isInterrupted)的地方,此时isInterrupted为false,所以循环继续,这样我们的while循环就永远也退不出去了。

换个写法:

  • class MyThread : Thread() {
  • private var queue: BlockingQueue<ByteArray> = LinkedBlockingQueue(5)
  • override fun run() {
  • try {
  • while (!isInterrupted) {
  • Log.i("ABCD", "11111")
  • val data = queue.take()
  • Log.i("ABCD", "22222")
  • }
  • } catch (e: Exception) {
  • Log.i("ABCD", "发生异常", e)
  • }
  • }
  • }

这次的区别在于,我们是把整个while给做了try catch,所以发生异常时,CPU直接就跳到catch的地方执行了,这样就跳出了while循环了,我项目最开始就是这么写的,所以好像也没什么问题。但是也还是不推荐使用isInterrupted做为while循环的控制条件的,我假设你while中调用了别人的函数,比如使用OkHttp框架请求网络,假设调用interrupt()函数时抛出的中断异常是在OkHttp的函数里面,而且别人做了try catch,则我们在while循环外面写的try catch就无法捕捉到这个异常了,那while循环就没法退出了,所以控制while循环还是需要使用自定义变量。所以,最佳做法应该是interrupt()函数和自定义变量一起用,如下:

  • class MyThread : Thread() {
  • private var needRun = true
  • private var queue: BlockingQueue<ByteArray> = LinkedBlockingQueue(5)
  • private var frameLossCount: Int = 0
  • fun addDatas(datas: Triple<ByteArray, Int, Int>) {
  • val offer = queue.offer(datas) // 如果队列满了,则放不进去且返回false
  • if (!offer) {
  • Logger.e("MyThread", "丢帧:${++frameLossCount}帧")
  • }
  • }
  • override fun run() {
  • try {
  • while (!isInterrupted && needRun) {
  • val data = queue.take() // 如果队列中没有数据,则一直阻塞,直到有数据
  • // TODO 做耗时操作
  • }
  • } catch (e: Exception) {
  • Log.i("ABCD", "发生异常", e)
  • }
  • }
  • fun close() {
  • needRun = flase
  • interrupt()
  • }
  • }

这种模式在音视频编解码时特别有用,当我们得到一些音视频数据时,只需要调用MyThread的addDatas方法来往队列中加入音视频数据,然后子线程中的run函数会取出列表中的数据,然后就可以做耗时的编解码的事情了,示例如下:

  • val myThread = Thread() // 创建线程对象
  • myThread.start() // 启动线程
  • myThread.addDatas(datas)// 往队列中放入数据
  • myThread.close() // 关闭线程
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门