正好最近没有工作,可以安心整理一些知识点,今天来分析一个问题:如何结束一个运行中的子线程?好了,我们先模拟你下问题,代码如下:
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;
}
}
如果有什么不对的地方还请各位看客提出大家共同进步,谢谢