线程
一、简介
线程是 CPU 调度的最小单位。当一个程序第一次启动的时候,Android 会启动一个 Linux 进程和一个主线程。Android 中所有的组件都在主线程中实例化。主线程主要负责处理 UI 相关的事件,所以又被叫做 UI 线程。在 Android 中主线程是不能够做耗时操作的,子线程是不能够更新 UI 的。与进程的区别:
二、创建多线程
2.1 继承 Thread 类
优点:实现简单,只要继承 Thread 类,并重写 run 方法就可以实现多线程。
缺点:
不适合资源共享。一个线程等于一个实例,相对独立,没办法进行资源共享;
消耗资源。Thread 线程相当于一次性消耗品,一个线程等于一个耗时任务,1 个耗时任务执行完毕,线程会自动销毁。有 100 个耗时任务,就需要开启 100 个线程。多次创建和销毁十分消耗系统资源。
具体使用:
// 步骤 1:创建线程类(继承自 Thread 类)
class MyThread extends Thread{
@Override
public void run(){
// 步骤 2:从写 run 方法
}
}
// 步骤 3:创建线程对象,即 实例化线程类
MyThread mt=new MyThread(“线程名称”);
// 步骤 4:通过 线程对象 控制线程的状态,如 运行、睡眠、挂起 / 停止
mt.start();
可复用,代码量大
使用匿名类:
new Thread(“ 线程名称 ”) {
@Override
public void run() {
// doSomeThing
}.start();
不可复用,简介
注意:Thread 调用 run() 方法,就是一个普通的方法,失去线程的特性;start() 方法才会启动一个线程。
2.2 实现 Runnable 接口
优点:
适合资源共享。Runnable 的代码可以被多个线程(Thread 实例)共享,适合多个线程共同处理同一资源的情况;
灵活。一个类可以继承多个接口,避免了继承 Thread 的单继承局限性。
Java 中真正能创建新线程的只有 Thread 类对象。通过实现 Runnable 的方式,最终还是通过 Thread 类对象来创建线程。所以对于实现了 Runnable 接口的类,称为线程辅助类;Thread 类才是真正的线程类。
具体使用:
// 步骤 1:创建线程辅助类,实现 Runnable 接口
class MyRunnable implements Runnable{
@Override
// 步骤 2:复写 run(),定义线程行为
public void run(){
}
}
// 步骤 3:创建线程辅助对象,即 实例化 线程辅助类
MyRunnable mr=new MyRunnable();
// 步骤 4:创建线程对象,即 实例化线程类;线程类 = Thread 类;创建时通过 Thread 类的构造函数传入线程辅助类对象
Thread td=new Thread(mr);
// 步骤 5:通过 线程对象 控制线程的状态,如 运行、睡眠、挂起 / 停止
td.start();
使用匿名类:
Runnable mt = new Runnable() {
@Override
public void run() {
}
};
Thread mt1 = new Thread(mt, “ 窗口 1 ”);
mt1.start();
2.3 AsyncTask
作用:实现工作线程 & 主线程(UI 线程)之间的通信,即:将工作线程的执行结果传递给主线程,从而在主线程中执行相关的 UI 操作。从而保证线程安全。优点:
方便实现异步通信。不需使用“任务线程(如继承 Thread 类)+ Handler”的复杂组合;
节省资源。采用线程池的缓存线程 + 复用线程,避免了频繁创建 & 销毁线程所带来的系统资源开销。
缺点:
线程是串行执行的(即后台只有一个线程);
虽然 AsyncTask 的任务执行是通过线程池,线程池的核心线程,非核心线程规格大于 1,但是后台运行的线程也只有一个,这是因为 AsyncTask 的任务管理线程池是 串行的 即,执行完一个任务才会执行下一个任务。
存在内存泄漏的风险。
类定义:
public abstract class AsyncTask<Params, Progress, Result> {
…
}
// 类中参数为 3 种泛型类型
// 整体作用:控制 AsyncTask 子类执行线程任务时各个阶段的返回类型
// 具体说明:
// a. Params:开始异步任务执行时传入的参数类型,对应 excute()中传递的参数
// b. Progress:异步任务执行过程中,返回下载进度值的类型
// c. Result:异步任务执行完成后,返回的结果类型,与 doInBackground() 的返回值类型保持一致
// 注:
// a. 使用时并不是所有类型都被使用
// b. 若无被使用,可用 java.lang.Void 类型代替
// c. 若有不同业务,需额外再写 1 个 AsyncTask 的子类
}
AsyncTask 核心 & 常用的方法如下:
使用步骤:
创建 AsyncTask 子类 & 根据需求实现核心方法
创建 AsyncTask 子类的实例对象(即 任务实例)
手动调用 execute(()从而执行异步线程任务。同一个 AsyncTask 实例对象只能执行 1 次,若执行第 2 次将会抛出异常
注意:
AsyncTask 是基于线程池进行实现的, 当一个线程没有结束时, 后面的线程是不能执行的。同时,AsyncTask 不与任何组件绑定生命周期,在 Activity 或 Fragment 中使用 AsyncTask 时,最好在 Activity 或 Fragment 的 onDestory 方法里面调用 cancel(boolean);
if (myAsyncTask != null && myAsyncTask.getStatus() == Status.RUNNING) {
//cancel 方法只是将对应的 AsyncTask 标记为 cancelt 状态, 并不是真正的取消线程的执行.
myAsyncTask.cancel(true);
}
若 AsyncTask 被声明为 Activity 的非静态内部类,当 Activity 需销毁时,会因 AsyncTask 保留对 Activity 的引用而导致 Activity 无法被回收,最终引起内存泄露。AsyncTask 应被声明为静态内部类;
当 Activity 重新创建时(屏幕旋转 / Activity 被意外销毁时后恢复),之前运行的 AsyncTask(非静态的内部类)持有的之前 Activity 引用已无效,故复写的 onPostExecute() 将不生效,即无法更新 UI 操作。所以,在 Activity 恢复时的对应方法需要重启任务线程。
原理:
参考原文:Android 多线程:AsyncTask 的原理 及其源码分析
线程池
Carson_Ho 的原文地址:Android 多线程:线程池 ThreadPool 全面解析