1,Thread
线程是Java语言的一个概念,是实际执行任务的最小单元
public class MyThead extends Thread {
@Override
public void run() {
super.run();
//执行具体的业务逻辑
}
}
启动线程
public void startThead() {
MyThead thead = new MyThead();
thead.start();
}
2,实现接口,并且实现run方法
public class MyRunable implements Runnable {
@Override
public void run() {
//具体的业务逻辑
}
}
启动runable
Thread thread = new Thread(new MyRunable());
thread.start();
Android应用中各种类型的线程,本质都是Linux系统的pthreads,在应用层分为各种线程
- 主线程:也称为UI线程,随着应用的启动而启动。主线程用来运行Android组件,同时刷新UI屏幕上的UI元素,只有主线程才能更新UI,因为Android的UI工具包不是线程安全的
- Binder线程:用于不同进程之间的通信,每个进程都维护着一个线程池,用来处理其他进程发来的消息,这些进程包括系统服务,Intents,ContentProdiver,Service等。一个典型的需要是使用Binder线程的场景是应用程序提供给一个给其他进程通过AIDL接口绑定的service。
- 后台线程:应用程序创建的线程都是后台线程,在Android框架里,WindowManager赋予了主线程只能更新UI的限制,而其他线程不能更新UI
2,HanderThead
HandlerThread是一个集成了Looper和MessageQueue的线程,当启动HandlerThread的时候,会同时创建一个looper和MessageQueue,然后等待消息去处理,下面是run方法的源码
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
它的使用方法和普通的线程是一样的,方法如下:
public void usehandlerThread() {
HandlerThread thread = new HandlerThread("use-handlethread");
thread.start();
Handler mHandler = new Handler(thread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
}
HandlerThread只有一个消息队列,队列的消息是顺序执行的,因此是线程安全的,当然吞吐量会收到一定的影响.
HandlerThread的内部机制确保了,在创建lopper和发送消息之间不存在竞态条件,这是通过HandlerThread.getLooper()实现为一个阻塞操作实现的,只有当HandlerThread准备好接受消息的时候才会返回
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// 如果线程已经启动,那么在looper准备好之前,应该先等待
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
如果某些业务下,需要在HandlerThread开始接收消息前执行一些操作,比如初始化Handler,可以继承HandlerThread来做
public class MyHandlerThread extends HandlerThread {
public MyHandlerThread(String name) {
super("MyHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
}
private Handler mHandler;
@Override
protected void onLooperPrepared() {
super.onLooperPrepared();
mHandler = new Handler(getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
}
public void uploadMsg(Message message) {
if(mHandler != null)mHandler.sendMessage(message);
}
}
3,AsyncQueryHander
4,IntentService
为了能够在Service内实现在子线程中执行耗时任务,Android
引入了IntentService,但依旧使用的是HanderThread
本身就是一个Service,可以通过context.startService启动
IntentServie是一个抽象类,需要我们使用前继承,并实现onHandleIntent(Intent intent);方法,同时在子类的构造方法中调用super(String name)传入子类名字
public class MyIntentService extends IntentService {
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public MyIntentService(String name) {
super(MyIntentService.class.getName());
setIntentRedelivery(true);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
// 这个方法在后台线程中调用,原因是IntentService内部的handler绑定的是自线程的looper
}
}
上面代码中setIntentRedelivery方法如果设置为true,那么IntentService的onstartCommand方法将返回START_REDELIVER_INTENT,这时如果onHandleIntent在返回前被进程杀死掉了。那么进程将重新启动,Intent将重新投递。
5,Java线程池 Executer Framwork
我们知道创建和销毁对象,是存在开销的(例如线程),这些会影响我们的性能,使用Java Executer框架可以通过线程池解决这一问题,Executer框架提供了如下能力
- 创建工作线程,同时通过队列来控制能够在这些线程执行任务的个数
- 检测导致线程意外终止的错误
- 等待线程执行完毕并获取执行结果
- 批量执行线程,并通过固定的顺序获取执行结果
- 在合适的时机启动后台线程,从而保证线程执行的结果很快反馈给用户
Executer是一个接口,主要目的就是分离任务的创建和执行,实现上面的功能点
public interface Executor {
void execute(Runnable command);
}
当需要实现自己的线程池的时候。只有实现Executor接口,并实现execute方法,最简单的实现就是创建线程执行任务
public class MyExecutor implements Executor {
@Override
public void execute(Runnable command) {
new Thread(command).start();
}
}
当然实际应用的Executor很少是这么简单的,需要增加类似队列,任务优先级等功能,最终实现线程池
线程池是任务队列和工作线程的集合,这俩者结合起来实现生产者和消费者模式
Executor已经为我们提供了几个预定义的线程池实现,如下
-
固定大小的线程池,通过Executors.newFixedThreadPool(n),n表示线程池中线程的大小
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
-
可变大小的线程池,通过Executors.newCachedThreadPool()创建
当有新的任务需要处理的时候,线程会创建新的线程来处理它
空闲的线程会等待60s来执行新任务,当没有可执行的任务,就会自动销毁
因此线程池的大小会根据队列的大小变化而变化
- 单个线程的线程池,通过Executors.newSingleThreadExecutor()创建,这个线程池中永远只有一个线程串行的执行任务队列中的任务
以上就是预定义的线程池,它们都是基于ThreadPoolExecutor类的基础上构建的,而通过ThreadPoolExecutor我们可以自定义线程池的一些行为。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
- corePoolSize:核心线程数。核心线程会一直存在线程池中,即使当前没有任务需要处理,当线程数小于核心线程数,即使当前有空闲的线程,线程池也会优先创建新的线程来执行新任务,这样做的好处是无需缓存
- maximumPoolSize:最大线程数。当线程数大于核心线程数,而且任务队列已经满了,这是线程池会创新新的线程来执行任务,直到线程数等于最大线程数为止
- keepAliveTime:线程空闲存活时间。当线程的空闲时间超过keepAliveTime时间时,线程会被销毁,知道线程数等于核心线程数
- unit:keepAliveTime的时间单位。可选的值有TimeUnit的枚举值
- workQueue 线程池锁使用的任务缓存队列
6,AsyncTask
AsyncTask时在Executors框架基础上的封装,它实现了耗时任务移动到工作线程中执行,同时提供方便的接口实现工作线程和主线程的通信
public class MyAsyncTask extends AsyncTask<Integer, Integer, String> {
@Override
protected void onPreExecute() {
super.onPreExecute();
//主线程
}
@Override
protected String doInBackground(Integer... integers) {
return null;
}
@Override
protected void onProgressUpdate(Integer[] values) {
super.onProgressUpdate(values);
//主线程
}
@Override
protected void onPostExecute(String o) {
super.onPostExecute(o);
//主线程
}
}
我们知道在不同的Android版本中对AsycTask的实现不尽相同,在不同的版本的表现区别如下
API level | execute方法 | executeOnExecutor方法 |
---|---|---|
1~3 | 串行执行 | 没有这个方法 |
4~10 | 并行执行 | 没有这个方法 |
11~12 | 并行执行 | 串行或者并行 |
13+ | 串行执行 | 串行或者并行 |
可以看到在api 13+建议使用executeOnExecutor方法代替execute
一个应用中所使用的所有Async实例会共享全局的属性,也就是说如果AsyncTask中的任务时串行执行,那么应用中的所有AsyncTask都会排队,只有等前一个任务执行完成,才会执行下一个任务
如果AsyncTask执行的是异步任务,那么在4核CPU系统上最多也只有5个任务同时进行,其余任务需要在队列中排队,等待空闲的线程,原因是AsyncTask中指定的核心线程数是CPU核数+1
7,Loader
Loader 是Android3.0开始引入的一个异步数据加载框架,这里暂时不做更多介绍,以后补充
网友评论