您的位置:首页 > 其它

多线程开发(一)-安卓系统的线程

2016-06-08 14:00 357 查看

第1节 线程概述

安卓应用只有一个主线程-各个组件都是在这个线程中运行。作为组件的之一的Activity就是在这个线程中更新应用界面的,例如,用户点击界面上的一个按钮,按钮得到响应,整个过程就是在这个主线程里。所以这个主线程绝对不可以做耗时的操作。假如在按钮中做了耗时的操作,那么当它进行耗时操作的时候,你去点击界面上的其它按钮是不会有反应的,就好像程序冻在了那里。

我们的代码一旦连续占用这个线程超过一定的时间,系统就会弹出“程序无响应的”提示,这个提示叫做
ANR
-Applicatin No Response。



这就好比你在正在做一件事情A,突然另一件事情B来打扰你,你不得不停下手头的工作来完成,做完了才能继续之前的工作;这时如果有另外一个人(另一个线程)来帮助你,把事情B全部包揽了,那你就不用分心了。当另一个人把事情B做完后,告诉你一声就可以了。

启动一个新的线程,分担耗时工作的方法是一种异步操作:我让你帮我做一件事情,布置任务后,我就去做其他的事情了,等你做完了再告诉我结果;

与它对应的是同步操作:我让你帮我做一件事情,布置任务后,我啥也不做,就等着你做完了告诉我结果;



例如,一个视频播放应用,获取视频信息所需要的时间是个不能确定的事情。如果视频很少,也许几十毫秒就能完成,如果视频很多(比如几十个),也许就要花二十多秒。

因此,我们可以考虑把获取视频信息的操作放到一个单独的线程thread中进行。启动一个新线程-工作线程thread-查询视频信息,查询完成后,工作线程再将结果通知到主线程,让主线程将查询到结果的结果显示到界面上。因为界面的更新一定要在主线程中进行,不能在别的线程修改,否则系统后提示运行错误。因此我们一定要将查询的结果发送给主线程,让主线程处理界面的更新。

这里就涉及到了线程的创建,工作分配,以及它们之间的配合-信息的传递。

第2节 线程Thread

一个Activity运行在一个主线程上,它不能进行耗时巨大的操作,也不能执行耗时不确定的操作。因此需要在新的线程中进行这些耗时的操作,这种进行耗时操作的线程称作工作线程。

2.1 Thread的简单使用

创建一个新的线程,

创建一个
Runnable
,重写它的
run()
函数,这个函数里用来进行耗时的操作;

@Override
public void run() {
//耗时的操作
while(xxx)
{

}
}
};


Runnable
为参数,创建一个
Thread
,调用
Thread
start()
方法后,新线程就运行起来了,并执行
Runnable
中的
run()
函数;

Thread thread = new Thread(runnable);
thread.start();


run()
函数执行完毕后,新线程就退出;

在线程执行耗时操作的过程中,有时要取消这个线程的操作,

最好的办法是在
run()
函数中增加一个标志位,工作线程会随时检查这个标志位是否被设置上,如果设置上了,就让
run()
函数,立即返回,

//设置标志位,为退出线程使用
boolean needStop = false;
......
Runnable runnable = new Runnable() {

@Override
public void run() {
//耗时的操作
while(!needStop)
{

}
}
};


如果主线程要取消这个线程的工作,修改这个标志位就好了,

needStop = true;


2.2 Thread的创建

Thread可以拥有不同的优先级,从低到高有10级。操作系统根据线程的优先级来进行资源调度,优先为优先级高的线程分配CPU资源。在默认情况下,新创建的线程使用默认的优先级
NORM_PRIORITY
。设置优先级的方式很简单,

Thread thread = new Thread(runnable);
thread.setPriority(Thread.NORM_PRIORITY);


可以为线程取名字,当我们用
Android Monitor
工具调试应用的时候,就能看到创建线程时它的名字,方便我们观察、调试程序,

Thread thread = new Thread(runnable, "新线程的名字");




有时,一个应用会同时启动多个Thread,在创建它们的时候可以为它们设置
ThreadGroup
参数,将它们分成一组,便于对这些线程进行统一的管理。比如,中断这个组里所有线程的运行;

ThreadGroup group = new ThreadGroup("线程组");
Thread thread1 = new Thread(runnable, group);
thread1.start();
Thread thread2 = new Thread(runnable, group);
thread2.start();
Thread thread3 = new Thread(runnable, group);
thread3.start();

//中断3个线程的执行
group.interrupt();


2.3 Thread的停止

Thread的停止就是指这个线程的退出。线程的退出原因无外乎两种,

工作线程的工作完成了;

工作线程虽然正在进行耗时工作,但是被取消了,要提前结束;

2.3.1 正常退出

Runnable
中的
run()
函数执行完并返回后,当前的Thread就退出了。

2.3.2 使用标志位

run()
函数中增加一个标志位,工作线程会随时检查这个标志位是否被设置上,如果设置上了,就让
run()
函数,立即返回,

//设置标志位,为退出线程使用
boolean needStop = false;
......
Runnable runnable = new Runnable() {

@Override
public void run() {
//耗时的操作
while(!needStop)
{

}
}
};


如果主线程要取消这个线程的工作,修改这个标志位就好了,

needStop = true;


2.3.3 使用interrupt()方法

interrupt()
是线程提供的一个标准的线程退出方法,如果当前的工作线程正被
Object.wait
Thread.join
Thread.sleep
阻塞,那么使用
thread.interrupt()
之后,正在运行的线程会抛出一个
InterruptedException
异常,

Runnable runnable = new Runnable() {

@Override
public void run() {
...

while(!needStop)
{

try {
......
Sleep(1000);

} catch ( InterruptedException e ) {
needStop = true
}

}
}
};


不过
interrupt()
方法并不是万能的,不是所有阻塞情况下都能够让线程立即退出。

例如当该线程正在用
ServerSocket
accept()
方法等待连接的时候,即使调用了这个工作线程的
interrupt()
方法,该线程还是不会抛出异常的。

它只对
Object.wait
Thread.join
Thread.sleep
这几种阻塞有效果。对于网络读取数据时代阻塞状态解除是没有效果的。

*对于网络读取数据时造成的阻塞,我们会在以后相应的章节详细介绍解决方法。

2.4 线程之间的同步

线程间的同步就是指线程A执行到一个地方的时候,停了下来,等待线程B的执行结果;等线程B执行出结果后,线程A才能继续执行。

2.4.1 join()方法

最常见的就是主线程的执行,依赖于工作线程的退出。

主线程启动了工作线程以后,有时候需要等到工作线程结束以后再进行接下来的操作。

例如一个Activity,在
onCreate()
的时候创建了一个工作线程Thread B;后来在用户的要求下,Activity退出,要被销毁了;销毁Activity时,主线程要等到Thread B执行完了才能继续接着进行剩下的清理工作,那么Activity可以在它的onDestroy()函数中可以使用
join()
方法,等待子线程的结束,



private Thread mTreadB;

@Override
protected void onCreate() {
super.onCreate();

mThreadB = new Thread(runnable);
mThreadB.start();
}

@Override
protected void onDestroy() {
super.onDestroy();

mThreadB.join();
//进行剩下的清理操作
}


join()
方法,会一直处于阻塞状态,直到线程B退出。

onDestroy()
中使用
join()
有天生的缺点:不能在主线程中进行耗时不可控的操作(例如这里等待工作线程执行完毕),万一工作线程的退出花费了很长的时间,那就有问题了。这里的场景只是用来举一个例子而已。

2.4.2 wait()方法

利用
Object
wait()
方法实现线程间的同步,需要线程之间共享一个“锁”-
Object
对象。

final Object lock = new Object();


当主线程A执行到一个阶段的时候,如果要等待线程B,就把锁置于等待状态,

lock.wait()


此时,线程A就出于阻塞的状态,不能往下执行了。

线程B继续它的运行,当它执行到一个阶段的时候,将锁设置成放行状态,

lock.notify();


此时,线程A的阻塞状态解除,可以继续往下执行了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: