android源码解析(4)--如何结束运行中的分线程

正好最近没有工作,可以安心整理一些知识点,今天来分析一个问题:如何结束一个运行中的子线程?好了,我们先模拟你下问题,代码如下:

     public void login(final String userName, final String password, final OnLoginListener onLoginListener) {
        onLoginListener.onLoginStart();
        mThread=new Thread(new Runnable() {
            @Override
            public void run() {
                Log.e("TAG", "run: 分线程开始");
                SystemClock.sleep(1000);
                Log.e("TAG", "run: 111111111");
                SystemClock.sleep(1000);
                Log.e("TAG", "run: 222222222");
                SystemClock.sleep(1000);
                Log.e("TAG", "run: 333333333");
                SystemClock.sleep(1000);
                Log.e("TAG", "run: 4444444444");
                SystemClock.sleep(1000);
                Log.e("TAG", "run: 5555555555");
                SystemClock.sleep(1000);
                Log.e("TAG", "run: 6666666666");
                SystemClock.sleep(1000);
                Log.e("TAG", "run: 77777777777");
                SystemClock.sleep(1000);
                Log.e("TAG", "run: 88888888888");
                SystemClock.sleep(1000);
                Log.e("TAG", "run: 99999999999");
                SystemClock.sleep(1000);
                Log.e("TAG", "run: 结束了");
            }
        });
        mThread.start();
    }
代码是不是很简单,这个方法就是开启分线程模拟耗时操作。问题来了,假如这个线程跑到log为5的地方了,我不想跑了,要求马上结束掉线程怎么办?先不要往下看,自己想想,如何才能结束呢?

          第一种:既然你是个线程,那么肯定地反映就是哥们写了个线程函数,不可能只有开始没有停止吧?那么我们当然要去Thread的源码看看了,一看,还真有:

    /**
     * Requests the receiver Thread to stop and throw ThreadDeath. The Thread is
     * resumed if it was suspended and awakened if it was sleeping, so that it
     * can proceed to throw ThreadDeath.
     *
     * @deprecated because stopping a thread in this manner is unsafe and can
     * leave your application and the VM in an unpredictable state.
     */
    @Deprecated
    public final void stop() {
        stop(new ThreadDeath());
    }
是不是瞬间感觉来了救星了。但是仔细看看上面的注解,不安全这么结束线程,并且这个方法已经过时不建议使用了,完了,这哥们太不地道了,过时了也没给个替代的方法,怎么结束?

第二种:网上说,用如下方法可以结束:

    mThread.interrupt();
翻译过来我靠,这就是线程中断的意思。仿佛又看到救星了,来吗那就测试一下看看吧:
05-29 10:51:17.671 17270-18234/bfkj.com.mvpdemo E/TAG: run: 分线程开始
05-29 10:51:18.671 17270-18234/bfkj.com.mvpdemo E/TAG: run: 111111111
05-29 10:51:18.929 17270-17270/bfkj.com.mvpdemo E/TAG: stopLogin: 停止分线程
05-29 10:51:19.671 17270-18234/bfkj.com.mvpdemo E/TAG: run: 222222222
05-29 10:51:20.671 17270-18234/bfkj.com.mvpdemo E/TAG: run: 333333333
05-29 10:51:21.671 17270-18234/bfkj.com.mvpdemo E/TAG: run: 4444444444
05-29 10:51:22.672 17270-18234/bfkj.com.mvpdemo E/TAG: run: 5555555555
05-29 10:51:23.672 17270-18234/bfkj.com.mvpdemo E/TAG: run: 6666666666
05-29 10:51:24.672 17270-18234/bfkj.com.mvpdemo E/TAG: run: 77777777777
05-29 10:51:25.672 17270-18234/bfkj.com.mvpdemo E/TAG: run: 88888888888
05-29 10:51:26.673 17270-18234/bfkj.com.mvpdemo E/TAG: run: 99999999999
05-29 10:51:27.673 17270-18234/bfkj.com.mvpdemo E/TAG: run: 结束了
        是不是幼小的心灵又一次受到了打击,还是不行。其实这个方法就是官方建议和使用的方法,具体可以参考这篇博客:http://www.ibm.com/developerworks/cn/java/j-jtp05236.html     但是呢,不是我们想要的结果,那我们只能自己想办法了。

       第一种方法:设置标志符,不停的去检查标志符,如果标识符改变了,那么就停止执行下面的代码,线程被系统回收掉,伪代码如下:

    private Thread mThread;
    private boolean mFinish=false;
    @Override
    public void login(final String userName, final String password, final OnLoginListener onLoginListener) {
        onLoginListener.onLoginStart();
        mThread=new Thread(new Runnable() {
            @Override
            public void run() {
                while (!mFinish){
                    SystemClock.sleep(1000);
                }
            }
        });
        mThread.start();
    }
然后外部来主动改变标志符,让线程停止,这一点我们可以从volley的源码中找到答案:

     RequestQueue requestQueue = Volley.newRequestQueue(this);
        StringRequest stringRequest=new StringRequest("", new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
            }
        });
        requestQueue.add(stringRequest);
        //假如说上面有一百个请求等待处理,然后我们执行停止
        requestQueue.stop();
看看stop方法里做了什么:
    /**
     * Stops the cache and network dispatchers.
     */
    public void stop() {
        //这是一个读取缓存的线程
        if (mCacheDispatcher != null) {
            mCacheDispatcher.quit();
        }
        //这是四个联网请求数据的线程
        for (int i = 0; i < mDispatchers.length; i++) {
            if (mDispatchers[i] != null) {
                mDispatchers[i].quit();
            }
        }
    }
我们只关注联网请求的四个线程都做了什么,线程执行了quit方法:
    /**
     * Forces this dispatcher to quit immediately.  If any requests are still in
     * the queue, they are not guaranteed to be processed.
     */
    public void quit() {
        //这是一个线程停止的标志位
        mQuit = true;
        //然后调用自己的停止方法
        interrupt();
    }
标志位置为true了,然后自己停止了,然后我们看看run方法:

    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Request<?> request;
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            // release previous request object to avoid leaking request object when mQueue is drained.
            request = null;
            try {
                // Take a request from the queue.
                request = mQueue.take();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }
            //下面的不关心
        }
    }

可以看到这里根据标志位就跳出来了,结束了。但是这里其实是有个问题的,就是如果说停止的那一瞬间刚好执行过了这个方法,那么这次联网还是会正常执行的,说白了还是没有真正的停止线程,而只是停止了线程执行下次联网任务(这里如果理解不对了欢迎指正);

下面再以Asynctask中的注解来看看谷歌官方是怎么建议结束分线程的:

 * <p>A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking
 * this method will cause subsequent calls to {@link #isCancelled()} to return true.
 * After invoking this method, {@link #onCancelled(Object)}, instead of
 * {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])}
 * returns. To ensure that a task is cancelled as quickly as possible, you should always
 * check the return value of {@link #isCancelled()} periodically from
 * {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)</p>
也就是说,他们也是建议我们要不停的去检查我们设置的标志位是否改变了,可以借鉴一下!


        第二种方法:就是用线程池来管理,当然看了半天线程池源码也没看懂是如何管理的,这里就暂时不分析了,有时间再继续(其实我觉得线程池也不能解决,所以先放着)。

上面分析了如何停止线程,那么我们继续分析一个问题,那就是如何暂停线程呢?很多时候我们的页面执行了onPause方法后我们所开的那些分线程也可以随之暂停工作,等到到了我们的页面重新执行onResume方法后继续为我们服务,那么应该怎么做呢?很抱歉线程中虽然也提供了相应的方法,不过具体使用中发现有问题,也是并不能真正的实现暂停恢复的实际效果,所以我们需要借助其他类来实现,下面仅提供一种方法:

  public class TestThread extends Thread {
    /**
     * 辅助控制暂停、继续的变量
     */
    private Object mCrotalObject = new Object();
    /**
     * 暂停线程的标志
     */
    public boolean mPauseThread = false;
    /**
     * 停止结束线程的标志
     */
    public boolean mStopThread = false;

    /**
     * 这里模拟进行耗时操作,实际应用当中可以根据具体逻辑来自己设定节点判断是否需要停止、结束线程
     */
    @Override
    public void run() {
        while (true){
            if(waitOrStopThread()){
                return;
            };
            SystemClock.sleep(200);
        }
    }

    /**
     * 判断是否需要暂停、停止线程操作
     * @return
     */
    private boolean waitOrStopThread() {
        if(mStopThread){
            return true;
        }
        if(mPauseThread){
            try {
                mCrotalObject.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return false;
        }
        return false;
    }



    /**
     * 线程的恢复
     */
    public void onResume() {
        mPauseThread = false;
        mCrotalObject.notify();
    }

    /**
     * 线程的暂停
     */
    public void onPasue() {
        mPauseThread = true;
    }

    /**
     * 线程的销毁
     */
    public void onDestory() {
        mStopThread = true;
    }
  }
如果有什么不对的地方还请各位看客提出大家共同进步,谢谢



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值