1、 简述
本文主要用作对多线程知识的回顾记忆。涵盖了日常开发常见的Java中的线程、线程池、锁中的概念以及Android 中AsyncTask、HandlerThread、IntentService的原理分析等。力求通过定义概念的罗列以及简单案例的实现让自己和大家有个基本的认识,Android应用层封装的各种利于开发者开发的多线程帮助工具如AsyncTask等都离不开java基础核心多线程的支持,虽然我们使用起来就那么几个方法开发效率很高,但我们在闲下来的时候可以去窥看里面到底做了些什么能解开一定的疑惑,还有谷歌工程师写的代码可以说是比较优雅的了。
多线程开发主要涉及:
1、线程基础
2、Handler
3、AsyncTask
4、HandlerThread
5、IntentService
2、多线程基础
2.1 线程基础
2.1.1 线程状态
这里我需要指出的是,有些地方将线程状态分为如下5中状态,而在java jdk中线程被定义了为6中状态。我们可以类比了看。
普遍定义的5中状态
java JDK 定义状态
2.1.1.1 阻塞
2.1.1.2 死锁
代码实现死锁
public class DeadLock implements Runnable{
private int flag;
// 注意... 这里对象必须是static类型的,保证锁住的是同一个对象
private static final Object object1 = new Object();
private static final Object object2 = new Object();
public DeadLock(int flag) {
this.flag = flag;
}
@Override
public void run(){
if (flag == 1){
synchronized (object1){
System.out.println("持有对象object1的锁,等待持有object2锁释放资源");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object2){
System.out.println("程序结束");
}
}
}
if (flag == 2){
synchronized (object2){
System.out.println("持有对象object2的锁,等待持有object1锁释放资源");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object1){
System.out.println("程序结束");
}
}
}
}
}
public class Client {
public static void main(String[] args){
new Thread(new DeadLock(1)).start();
new Thread(new DeadLock(2)).start();
//持有对象object1的锁,等待持有object2锁释放资源
//持有对象object2的锁,等待持有object1锁释放资源
// .......程序一直运行.......
}
}
2.1.2 线程基本使用
2.1.2.1 创建线程
本小节将简要介绍实现线程的三种方式:继承Thread,实现runnable,实现callable。这里有一点需要注意的是,实现callable是与线程池相关联的而callable很重要的一个特性是其带有返回值。当我们只需实现单线程时实现runnable更加利于线程程序的拓展。
public class HappyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("继承Thread");
}
}
public class JobRunnable implements Runnable {
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("实现runnable实现线程");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class EnjoyCallable implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(2000);
return "实现callable实现线程池";
}
}
public class Client {
public static void main(String[] args){
// 继承thread实现线程
new HappyThread().start();
// runnable 与 线程
new Thread(new JobRunnable()).start();
// callable 与 线程池
// 包含返回值
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> submit = executorService.submit(new EnjoyCallable());
try {
System.out.println(submit.get());
submit.cancel(true);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
//继承Thread
// ** 1s后 **
//实现runnable实现线程
// ** 2秒后 **
//实现callable实现线程池
}
虽然如上所示通过继承Thread类、实现runnable接口、通过线程池实现callable接口实现了线程,但其本质上都是将我们具体的运行逻辑放入Thread的run方法中的过程。
可以发现,Thread的run方法内部,会调用与之绑定的runnable的run方法。
2.1.2.2 守护线程
在线程开启前 调用 thread.setDaemon(true); 设定thread为当前线程的守护线程
使用案例
public class DaemonThreadClient {
public static void main(String[] args) throws InterruptedException {
Thread thread = new DaemonThread();
thread.setDaemon(true);
thread.start();
Thread.sleep(20);
System.out.println("主线程结束");
}
}
class DaemonThread extends Thread{
@Override
public void run() {
while (true){
try {
Thread.sleep(5);
System.out.println("守护线程运行中");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//output
//守护线程运行中
//守护线程运行中
//守护线程运行中
//守护线程运行中
//主线程结束
//Process finished with exit code 0
垃圾回收机制
垃圾回收线程就是一个经典的守护线程,当我们的程序中不再有任何运行的Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是JVM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。
关于守护进程的文章
Java中守护线程的总结
2.1.2.3 yield线程让步与join合并等待线程结束
线程让步【yield方法】让当前线程释放CPU资源,让其他线程抢占。
public class YieldClient {
public static void main(String[] args){
new ThreadA().start();
new ThreadB().start();
}
}
class ThreadA extends Thread{
public ThreadA(){
setPriority(2);
}
@Override
public void run() {
yield();
for (int i=0;i<10;i++){
System.out.println("ThreadA 低优先级的运行");
}
}
}
class ThreadB extends Thread{
public ThreadB(){
setPriority(8);
}
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println("ThreadB 高优先级的运行");
}
}
}
// output
// ThreadB 高优先级的运行
// ...
// ThreadB 高优先级的运行
// ThreadA 低优先级的运行
// ThreadA 低优先级的运行
// ThreadA 低优先级的运行
// ThreadA 低优先级的运行
// ...
// ThreadA 低优先级的运行
// ThreadA 低优先级的运行
// Process finished with exit code 0
join 会等待被调用该方法线程的结束再转来进行当前线程 也就是合并的意思
在该例中 主线程会等待joinA线程结束后再来执行主线程
线 程 阻 塞 等 待 但 不 会 释 放 锁 资 源 \color{red}{线程阻塞等待但不会释放锁资源} 线程阻塞等待但不会释放锁资源
public class JoinClient {
public static void main(String[] args) throws InterruptedException {
JoinAThread joinAThread = new JoinAThread();
joinAThread.start();
joinAThread.join();
System.out.println("主线程 开始");
Thread.sleep(10);
System.out.println("主线程 运行中");
Thread.sleep(10);
System.out.println("主线程 运行中");
System.out.println("主线程 结束");
}
}
class JoinAThread extends Thread{
@Override
public void run() {
super.run();
try {
System.out.println("JoinAThread 开始");
Thread.sleep(30);
System.out.println("JoinAThread 运行中");
Thread.sleep(30);
System.out.println("JoinAThread 运行中");
System.out.println("JoinAThread 结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//output
//JoinAThread 开始
//JoinAThread 运行中
//JoinAThread 运行中
//JoinAThread 结束
//主线程 开始
//主线程 运行中
//主线程 运行中
//主线程 结束
//
//Process finished with exit code 0
j
o
i
n
阻
塞
但
不
释
放
锁
资
源
解
释
\color{red}{join 阻塞但不释放锁资源解释}
join阻塞但不释放锁资源解释
下面将通过案例的方式解释join特征
构建一个主线程持有对象锁时,在这期间去合并等待子线程运行的过程,且这个子线程需要持有对象才能执行。
可以看到如下案例造成了死锁,主线程持有obj对象锁未释放,而这时候子线程执行又得持有对象锁obj,这就造成了死锁。也就是是说主线程执行join时并不会释放对象锁obj。
我们看下join的源码
那么其实join是当线程存活时调用wait()方法,而wait会释放调用者的锁对象,this.wait 那么调用的线程会释放锁资源。那么我们接着看下面的例子,这里会运行结束。
2.1.2.4 线程终止
public class InterruptClient {
public static void main(String[] args){
new InterruptThread().start();
}
}
class InterruptThread extends Thread{
@Override
public void run() {
super.run();
for (int i=0;i<10;i++){
if (isInterrupted()){
System.out.println("线程打断而结束");
return;
}
System.out.println("线程执行..."+i);
if (i == 2){
// 终止线程
interrupt();
}
}
}
}
//output
//线程执行...0
//线程执行...1
//线程执行...2
//线程打断而结束
//
//Process finished with exit code 0
2.1.3 线程关键知识点
2.1.3.1 sleep与wait区别
1、sleep在Thread类中定义为静态方法,wait在Object类定义为实例方法
2、wait 只能在同步上下文中调用wait方法,否则或抛出IllegalMonitorStateException异常;
sleep 不需要在同步方法或同步块中调用
3、sleep线程睡眠时不会释放已经获取的锁资源;当wait方法调用时,当前线程会释放已获取的对象锁资源,并进入等待队列,此时其他线程可以尝试获取该对象锁上资源。
可以借鉴以下文章:
Java中的sleep与wait区别
/**
* 测试 sleep 和 wait 区别
*/
public class Client {
public static void main(String[] args) throws InterruptedException {
Product product = new Product();
new Thread(new ChildSetRunnable(product),"set thread").start();
// 主线程睡眠,保证获取线程晚于设置线程的执行
Thread.sleep(500);
new Thread(new ChildGetRunnable(product),"get thread").start();
}
}
public class ChildGetRunnable implements Runnable {
private Product product;
public ChildGetRunnable(Product product) {
this.product = product;
}
@Override
public void run() {
// 运行加锁
System.out.println(Thread.currentThread().getName()+" run()");
synchronized (product){
System.out.println(Thread.currentThread().getName()+" "+System.currentTimeMillis()+" 读取价格 = "+product.getPrice());
}
}
}
public class ChildSetRunnable implements Runnable {
private Product product;
public ChildSetRunnable(Product product) {
this.product = product;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" run()");
synchronized (product){
try {
System.out.println(Thread.currentThread().getName()+" "+System.currentTimeMillis()+" 设置价格= 100 开始");
/**
* 1.测试sleep 释放CPU时间片,但仍然持有对product的锁资源
*/
Thread.sleep(3000);
/**
* 2.测试wait 释放CPU时间片,但是会释放锁资源
*/
//product.wait(3000);
product.setPrice("100");
System.out.println(Thread.currentThread().getName()+" "+System.currentTimeMillis()+" 设置价格= 100 结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.1.3.2 ReentrantLock公平锁,还是不公平?lock原理,如果实现加锁?
2.1.3.3 线程切换的时候为什么耗费性能?
2.1.3.4 AQS ??
2.1.3.5 wait ¬ify vs ReentrantLock condition vs CountDownLatch 案例
具体某个对象锁的 wait & notify 方法类似 Condition 的 await以及signal方法,
前者阻塞等待都是释放锁,而唤醒后该线程都是获得锁资源的;
而门栓就类似于一个阀门
/**
* 写2个线程 线程1添加10个元素到容器中,线程2监听控件元素,当个数到5时 线程2提示并结束
*
* wait释放锁 & notify 不会释放锁
*/
public class Program {
private volatile int countSize = 0;
private Object object = new Object();
public void add(){
synchronized (object){
for (int i = 0;i<10;i++){
try {
Thread.sleep(1000);
System.out.println("执行添加 "+countSize);
} catch (InterruptedException e) {
e.printStackTrace();
}
countSize ++;
// 如果数量为5 进行通知...
if (countSize == 5){
// 虽然通知了 但是程序还在执行者 还没释放锁
object.notify();
try {
// 那么就让他等待一下呗
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public void size() {
// 必须锁定才行....
synchronized (object){
System.out.println("开始查询");
if (countSize != 5){
System.out.println("阻塞中");
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("结束咯");
object.notify();
}
}
public static void main(String[] args){
Program program = new Program();
new Thread(new Runnable() {
@Override
public void run() {
program.size();
}
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
program.add();
}
}).start();
}
// 开始查询
// 阻塞中
// 执行添加 0
// 执行添加 1
// 执行添加 2
// 执行添加 3
// 执行添加 4
// 结束咯
// 执行添加 5
// 执行添加 6
// 执行添加 7
// 执行添加 8
// 执行添加 9
//
// Process finished with exit code 0
}
/**
* ReentrantLock & Condition 实现阻塞等待
*/
public class ReenProgram {
private volatile int count = 0;
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void add() throws InterruptedException {
lock.lock();
try {
for (int i=0;i<10;i++){
count++;
Thread.sleep(500);
System.out.println("执行 "+count);
if (count == 5){
condition.signal();
System.out.println("运行线程处于等待被唤醒");
// await释放锁
condition.await();
}
}
}finally {
lock.unlock();
}
}
public void get() throws InterruptedException {
lock.lock();
try {
if (count != 5){
System.out.println("数量未达到5 等待");
condition.await();
}
// 通知阻塞等待线程
condition.signal();
System.out.println("继续执行了");
}finally {
lock.unlock();
}
}
public static void main(String[] args){
ReenProgram reenProgram = new ReenProgram();
new Thread(new Runnable() {
@Override
public void run() {
try {
reenProgram.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
reenProgram.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
// 数量未达到5 等待
// 执行 1
// 执行 2
// 执行 3
// 执行 4
// 执行 5
// 运行线程处于等待被唤醒
// 继续执行了
// 执行 6
// 执行 7
// 执行 8
// 执行 9
// 执行 10
//
// Process finished with exit code 0
public class ProgramTwo {
private volatile int size = 0;
private CountDownLatch countDownLatch = new CountDownLatch(1);
public void add(){
for (int i=0;i<10;i++){
try {
size++;
Thread.sleep(1000);
System.out.println("执行添加 "+size);
if (size == 5){
countDownLatch.countDown();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void size(){
System.out.println("开始查询");
if (size != 5){
try {
System.out.println("门拴住了");
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("结束咯");
}
public static void main(String[] args) throws InterruptedException {
ProgramTwo programTwo = new ProgramTwo();
new Thread(new Runnable() {
@Override
public void run() {
programTwo.size();
}
}).start();
Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run() {
programTwo.add();
}
}).start();
}
}
//开始查询
//门拴住了
//执行添加 1
//执行添加 2
//执行添加 3
//执行添加 4
//执行添加 5
//结束咯
//执行添加 6
//执行添加 7
//执行添加 8
//执行添加 9
//执行添加 10
//
//Process finished with exit code 0
生产者消费者问题
wait 和notify 和 synchronized综合运用
Java多种方式解决生产者消费者问题(十分详细)
public class Company {
// 设定数量为10
private static final int size = 10;
private volatile LinkedList<Integer> list = new LinkedList<>();
private final ReentrantLock lock = new ReentrantLock();
private final Condition noFull = lock.newCondition();
private final Condition noEmpty = lock.newCondition();
public void produce() throws InterruptedException {
lock.lock();
try {
// 当超过库存阻塞
while (list.size()+1 > size){
System.out.println("库存已满 生产者等待");
try {
noFull.await();
}catch (InterruptedException e){
e.printStackTrace();
}
}
// 500毫秒 生产一个产品
System.out.println("生产者生产:"+list.size());
list.add(list.size());
// 通知消费者消费
noEmpty.signalAll();
}finally {
lock.unlock();
}
}
// 消费 当库存为0阻塞
// 1s 消费一个
public void consume() throws InterruptedException {
lock.lock();
try {
while (list.size() == 0){
// 库存为空 消费者等待
System.out.println(Thread.currentThread().getName()+"库存为空 消费者等待");
try {
noEmpty.await();
}catch (InterruptedException e){
e.printStackTrace();
}
}
Integer last = list.getLast();
if (last != null){
System.out.println(Thread.currentThread().getName()+"消费者消费 "+list.get(last));
list.removeLast();
}
noFull.signalAll();
}finally {
lock.unlock();
}
}
}
public class Client {
public static void main(String[] args){
Company company = new Company();
new Thread(() -> {
while (true){
try {
Thread.sleep(3000);
company.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
while (true){
try {
Thread.sleep(3000);
System.out.print("");
company.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
while (true){
try {
Thread.sleep(500);
company.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
// output
//生产者生产:0
//生产者生产:1
//生产者生产:2
//生产者生产:3
//生产者生产:4
//Thread-0消费者消费 4
//Thread-1消费者消费 3
//生产者生产:3
//生产者生产:4
//生产者生产:5
//生产者生产:6
//生产者生产:7
//生产者生产:8
//Thread-0消费者消费 8
//Thread-1消费者消费 7
//生产者生产:7
//生产者生产:8
//生产者生产:9
//库存已满 生产者等待
//Thread-0消费者消费 9
//Thread-1消费者消费 8
//生产者生产:8
//生产者生产:9
//库存已满 生产者等待
//Thread-0消费者消费 9
//Thread-1消费者消费 8
//生产者生产:8
//生产者生产:9
//库存已满 生产者等待
//Thread-0消费者消费 9
//Thread-1消费者消费 8
//生产者生产:8
//生产者生产:9
//库存已满 生产者等待
//Thread-0消费者消费 9
//Thread-1消费者消费 8
//生产者生产:8
//生产者生产:9
//库存已满 生产者等待
//。。。。
2.2 线程池基础
2.2.1 线程任务的运行流程
2.2.2 线程池使用
下面通过一个案例来看下线程和线程池执行并发任务的区别。从运行结果上来看线程池会复用其维护的特定数量的线程来处理任务,且执行完后其进行等待。
/**
* 该程序展示线程与线程池的区别
*
* 复用线程执行任务
*/
public class ThreadVsPoolClient {
public static void main(String[] args){
ExecutorService service = Executors.newFixedThreadPool(2);
for (int i =0;i<4;i++){
new Thread(new ThreadRuun()).start();
service.submit(new PoolCall());
}
}
}
class ThreadRuun implements Runnable{
@Override
public void run() {
try {
Thread.sleep(500);
System.out.println("运行线程名 "+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class PoolCall implements Callable<String>{
@Override
public String call() throws Exception {
try {
Thread.sleep(500);
System.out.println("运行线程名 "+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
return "back";
}
}
// output
// 运行线程名 pool-1-thread-1
// 运行线程名 Thread-0
// 运行线程名 pool-1-thread-2
// 运行线程名 Thread-2
// 运行线程名 Thread-1
// 运行线程名 Thread-3
// 运行线程名 pool-1-thread-1
// 运行线程名 pool-1-thread-2
可借鉴的文章
Android开发之线程池使用总结
Android 线程池原理及使用
深入理解在Android中线程池的使用
(四)深入理解线程池底层原理
2.2.3阻塞队列使用
lockingQueue类图以及方法图
3. AsyncTask源码分析
定义
什么是AsyncTask?
一个异步处理的库,我们可以在子线程中做耗时操作在主线程中更新UI。
本质上就是一个封装了线程池和Handler的异步框架
方法解析
AsyncTask 原理
前面说AsyncTask是线程池和Handler的结合,那么我们需要带着这些问题去源码里找答案
1、线程池是怎么工作的?AsyncTask 里线程池有什么特别之处?
2、Handler又是在哪里切换线程的呢?如何切换的
3、前面提到的Task提供的方法在代码中怎么回调的?如何确定其运行环境?
首先确认任务开启的入口
execute是AsyncTask提供给开发者调用的包装方法,其具体实现在executeOnExecutor中,另外我们需要关注线程池=>sDefaultExecutor。
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
executeOnExecutor方法做了如下几件事:
1、检查线程状态,保证运行的任务一定是未开始的
当处于运行或者已完成状态时抛异常,这里证实了 我们不能重复的调用task.execute方法
2、修改运行状态,并在线程池执行前回调onPreExecute 方法
这里证实了: task提供的onPreExecute()方法,是在调用了 task.execute() 后,且在task任务开始前调用的,还有一点方法运行环境 == 主线程
3、开启线程池
我们看到代码里只是调用了 exec.execute(mFuture) ,这里就是线程池执行的方法入口。我们后面得去重点关注下sDefaultExecutor 以及其runnable接口 mFuture
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
// 如果是正在运行或者已经完成直接抛异常
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
接着分析线程池
前面分析到:Executor实现对象 == sDefaultExecutor ; 任务运行的Runnable == mFuture
所以我们去重点看这2者的具体实现
sDefaultExecutor是SerialExecutor的实体对象
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
前面 exec.execute(mFuture) 方法,就指向了SerialExecutor的execute方法。
// 静态内部类,持有外部类AsynTask的公共方法以及属性
private static class SerialExecutor implements Executor {
// 数组实现的双向队列,无容量限制,可自动扩容
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
// execute为同步方法
// 保证每次执行都会去上锁,保证runnable任务的顺序执行
public synchronized void execute(final Runnable r) {
// 添加任务
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
// 若当前无任务,则去队列取出一个执行
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
// 取出队列头部任务
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
从以上代码段我的看法是:
这里SerialExecutor线程池的作用就是将我们的任务按顺序加入到队列中,然后再将任务派发给真正执行任务的线程池THREAD_POOL_EXECUTOR;
由于SerialExecutor 的 execute加入了锁,保证了实际工作线程池里的任务是串形执行的。
理解下代码结构:
将我们具体的任务runnable从分派任务的runnable转发到了任务线程池中。我觉得这里有点代理的意思在里面。
前面提到我们实际工作的线程池是 THREAD_POOL_EXECUTOR,那我们就来具体看下它是怎么实现的。主要包括线程池数以及阻塞队列的构造选择问题。
// 获得CPU数量
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
// 我们希望在核心池中至少有2个线程,最多4个线程,希望比CPU计数少1个,以避免后台工作使CPU饱和
// 核心线程2-4 但最大值 介于可用的处理器数少1和4之间的最小值
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
// 最大线程池为 可用处理器2倍+1
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
// 空闲线程池存活30s
private static final int KEEP_ALIVE_SECONDS = 30;
// 初始化线程工厂
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
// 阻塞队列 选择 LinkedBlockingQueue,节约内存空间
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;
// 初始化 保证整个AsyncTask只存在这么一份工作线程池
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
这里可以观察到工作线程池是静态成员变量的方式,所以单个应用里也只会维护这么一个线程池,节约了内存空间。线程池我们已经分析完了接下来我们去分析具体的任务创建–mFuture。
// 保证原子操作的线程安全的boolean类型变量
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
// 可存储参数类型Params的Callable
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
// FutureTask构造函数
// 实际是callable<T>的包装类 增加了状态的标志
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public AsyncTask(@Nullable Looper callbackLooper) {
// ..... 此处省略了 handler 我们后面进行分析
// 初始化workRunnable变量mWorker
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
// 添加线程标志
mTaskInvoked.set(true);
Result result = null;
try {//设置后台优先级 低于普通线程
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
// 执行异步
// 所以 doInBackground是执行在子线程中的
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
// 若运行异常 设置取消的标志
mCancelled.set(true);
throw tr;
} finally {
//将运行结果通过handler发送到主线程中 得以更新UI
postResult(result);
}
return result;
}
};
mFuture = new FutureTask<Result>(mWorker) {
// 取消或者任务异常会回调该方法
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
任务是在构造函数中进行初始化的,AsyncTask构造了一个FutureTask是一个带有运行状态标识的Callable,并且可在任务结束或者异常时进行处理。
接下来我们来看看如何在子主线程中切换的,这自然离不开Handler了
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
// ....
}
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
// AsyncTask的finish方法
// 判断是否是手动设置了取消,执行onCancelled或onPostExecute
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
// AsyncTask中onProgressUpdate
protected void onProgressUpdate(Progress... values) {
}
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
// 处理结束标志 执行finiah方法,此时运行于主线程
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
// 进度更新消息执行onProgressUpdate
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
// 子线程中调用 publishProgress,发送MESSAGE_POST_PROGRESS通知
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
小结:
1、构造方法中初始化了Handler,且是在主线程中
2、handler实现了处理结束以及进程中的handlerMessage方法
3、onCancelled、onPostExecute会在MESSAGE_POST_RESULT的消息中回调,区别方式是否手动设置了cancel;而onProgressUpdate 方法会在 MESSAGE_POST_PROGRESS 中进行调用,而触发此消息的是 publishProgress 方法。所以从这一点得出:在doInBackground 子线程中,调用publishProgress可发送处理进度的消息,使得在主线程中执行onProgressUpdate方法;在程序执行完毕或者取消后 会在主线程中调用onCancelled或者onPostExecute以更新结果状态。
FutureTask 实现了Runnable和Future接口,内部包含有Callable接口,
作用就是检测线程的结束并可获取返回值。
原理是 在run方法中,调用callable的run方法获取结果通过set方法设置结果,然后在异常和正常结束时都调用 done方法检测线程运行结束。
源码总结
AsyncTask本质上是在静态的线程池执行任务然后通过Handler切换线程。SerialExecutor用于串形分派任务给THREAD_POOL_EXECUTOR,而THREAD_POOL_EXECUTOR作为真正执行任务的线程池,InternalHandler用于在线程池中发送RESULT以及PROGRESS消息,然后在主线程中依据消息类型执行AsyncTask中设置的回调方法onCancelled、 onPostExecute或onProgressUpdate。
可借鉴的文章
Android 多线程:AsyncTask的原理 及其源码分析
Android AsyncTask面试详解
4、HandlerThread源码分析
4.1 源码
public class HandlerThread extends Thread {
// 构造方法,设置线程名以及设置优先级
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
// 运行线程执行的方法
@Override
public void run() {
mTid = Process.myTid();
// 通过ThreadLocal来初始化looper以此来设定Thread与
// looper的一一对应关系
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
//开启looper轮询 获取message
Looper.loop();
mTid = -1;
}
// 获取looper,同步的作用是如果线程被启动,等待looper被创建完成
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
}
4.2 简单使用
可参考IntentService源码中对HandlerThread的运用
private void initHandlerThread() {
mHandlerThread = new HandlerThread("work");
// 开启线程...
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
// 消息处理很明显此时还是在子线程中呢....
// work 处理消息运行于 子线程work中
// 想要更新UI还得再post到 main线程
Log.e("handlerThread", Thread.currentThread().getName()+"");
super.handleMessage(msg);
}
};
}
// 记得退出handler
private void quitHandler(){
mHandlerThread.quit();
}
private void initListener() {
mBtnOne.setOnClickListener(v -> {
// 发送消息
if (mHandler !=null){
mHandler.sendEmptyMessage(0);
}
});
}
5、IntentService源码分析
5.1 简介与特点
IntentService是包含有HandlerThread的Service
特点
- 在主线程中发消息,在子线程中处理任务
- 单线程执行且由于looper其内部的任务是串形执行的.
- 任务检查机制,会在任务结束后自动关闭服务,避免了服务引起的内存泄漏问题
5.2 工作流程
我们看下IntentService的工作流程,启动一个IntentService它会在生命周期IntentService中创建HandlerThread初始化looper,handler等,在onStartCommand中即主线程中发送了消息,由于looper机制,该被添加到messageQueue中的message最终会转交到生成looper的子线程中进行处理,此时在子线程中会执行我们实现的onHandleIntent方法,在结束任务时会判断该服务是否有新的任务进入没有的话则关闭该服务否则继续执行。
5.3 源码分析
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// 获取Intent、处理消息
onHandleIntent((Intent)msg.obj);
// 关闭服务
// 带参数的stopSelf(int startId)会在所有任务执行完毕后将服务给停止。通常情况下调用stopSelf(int satrtId)方法不会立刻去执行停止服务的操作,会去判断最近执行任务的次数是否和startId相等,如果相等就立刻执行停止服务的操作
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
super.onCreate();
// 创建HandlerThread并启动
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
// 获取HandlerThread的looper对象,并创建Handler对象,该Handler运行于子线程中
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
// 调用onStart
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
// 发送消息
mServiceHandler.sendMessage(msg);
}
@Override
public void onDestroy() {
//结束looper
mServiceLooper.quit();
}
// 子线程处理任务
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
Q: 子线程也可以创建Looper?
HandlerThread很好的回答了该问题。
在看源码之前我们先回忆下线程状态:当我们调用 thread.start()此时线程处于就绪状态,当获取CPU调度后会执行线程 run方法。
// 构造方法设置线程优先级
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
@Override
public void run() {
mTid = Process.myTid();
// 在当前线程调用 Looper.prepare() 那么就会创建1个新的Looper,
//内部初始化MessageQueue 并将通过ThreadLocal将Looper与Thread绑定。
//而只有存在looper才能进行消息循环
Looper.prepare();
synchronized (this) {
// 通过ThreadLocal获取looper
mLooper = Looper.myLooper();
notifyAll();
}
// 设置线程优先级
Process.setThreadPriority(mPriority);
// 消息循环前等待工作
onLooperPrepared();
// 执行消息循环
Looper.loop();
mTid = -1;
}
// 结束消息循环 效率高
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
// 结束消息循环 线程安全
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}