IntentService
多线程的应用在Android 开发中是非常常见的,常用方法主要有:
-
集成Thread类
-
实现Runnable接口
-
AsyncTask
-
Handler
-
HandlerThread
-
IntentService
IntentService
定义: Android 里的一个封装类,继承四大组件之一 service
作用:处理异步请求 & 实现多线程
使用场景:线程任务 按照顺序,在后台执行。
最常见的场景,离线下载,但是不符合多个数据同时请求的场景,所有任务都在同一个Thread looper 里执行。
特别注意
-
若启动IntentService多次,那么每个耗时操作则以队列的方式在 IntentService 的 onHandleIntent回调方法中依次执行,执行完自动结束。
问题1:IntentService 如何单独开启1个新的工作线程
@Override
public void onCreate() {
super.onCreate();
// 1. 通过实例化HandlerThread新建线程 & 启动;故 使用IntentService时,不需额外新建线程
// HandlerThread继承自Thread,内部封装了 Looper
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
// 2. 获得工作线程的 Looper & 维护自己的工作队列
mServiceLooper = thread.getLooper();
// 3. 新建mServiceHandler & 绑定上述获得Looper
// 新建的Handler 属于工作线程 ->>分析1
mServiceHandler = new ServiceHandler(mServiceLooper);
}
/**
* 分析1:ServiceHandler源码分析
**/
private final class ServiceHandler extends Handler {
// 构造函数
public ServiceHandler(Looper looper) {
super(looper);
}
// IntentService的handleMessage()把接收的消息交给onHandleIntent()处理
@Override
public void handleMessage(Message msg) {
// onHandleIntent 方法在工作线程中执行
// onHandleIntent() = 抽象方法,使用时需重写 ->>分析2
onHandleIntent((Intent)msg.obj);
// 执行完调用 stopSelf() 结束服务
stopSelf(msg.arg1);
}
}
/**
* 分析2: onHandleIntent()源码分析
* onHandleIntent() = 抽象方法,使用时需重写
**/
@WorkerThread
protected abstract void onHandleIntent(Intent intent);
问题2 IntentService 如何通过onStartCommand() 将intent传递给服务, 依次插入到工作队列中的。
/**
* onStartCommand()源码分析
* onHandleIntent() = 抽象方法,使用时需重写
**/
public int onStartCommand(Intent intent, int flags, int startId) {
// 调用onStart()->>分析1
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
/**
* 分析1:onStart(intent, startId)
**/
public void onStart(Intent intent, int startId) {
// 1. 获得ServiceHandler消息的引用
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
// 2. 把 Intent参数 包装到 message 的 obj 发送消息中,
//这里的Intent = 启动服务时startService(Intent) 里传入的 Intent
msg.obj = intent;
// 3. 发送消息,即 添加到消息队列里
mServiceHandler.sendMessage(msg);
}
源码总结:
IntentService 本质 是 service + HanderThread
-
通过handlerThread单独开启一个工作线程,IntentService
-
创建一个内部Handler:ServiceHandler
-
绑定serviceHandler 与 IntentService
-
通过onStartCommand() 传递intent到ServiceHandler,依次插入Intent到工作队列中。并且逐个发送到给HanderIntent
-
通过onHandlerIntent ()依次处理所有Intent对象所对应的任务。
线程 关键字: Synchronized
它是java中一个关键字
被他修饰的方法可以保证同一时刻最多只有一个线程执行此方法,其他线程必须等待当前线程执行完该方法/代码块后才能执行该方法。
保证线程安全,解决多线程中的并发同步问题 实现的是阻塞型并发
原理:
monitor 监视器锁的本质,依赖于底层操作系统的互斥锁 实现。
其他控制并发/ 现成同步的方法
Lock ReentrantLock
CAS compre and Swap 乐观锁
// CAS的操作参数
内存位置(A)
预期原值(B)
预期新值(C)
// 使用CAS解决并发的原理:
// 1. 首先比较A、B,若相等,则更新A中的值为C、返回True;若不相等,则返回false;
// 2. 通过死循环,以不断尝试尝试更新的方式实现并发
// 伪代码如下
public boolean compareAndSwap(long memoryA, int oldB, int newC){
if(memoryA.get() == oldB){
memoryA.set(newC);
return true;
}
return false;
}
优点: 耗费资源少,相对于synchronized ,省去了挂起线程,恢复线程的开销,但是迟迟得不到更新,死循环对CPU资源也是一种浪费
考点:线程与进程的区别
假设:进程 = 桌子,单线程 = 1个人吃饭
-
单进程、单线程:一个人在一个桌子上吃饭
-
单进程、多线程:多个人在同一个桌子上一起吃饭
-
多进程、单线程:多个人每个人在自己的桌子上吃饭
一个进程可以拥有多个线程 ,多个线程可以共享他们隶属的同意进程的系统资源
ThreadLocal
// 1. 直接创建对象
private ThreadLocal myThreadLocal = new ThreadLocal()
// 2. 创建泛型对象
private ThreadLocal myThreadLocal = new ThreadLocal();
// 3. 创建泛型对象 & 初始化值
// 指定泛型的好处:不需要每次对使用get()方法返回的值作强制类型转换
private ThreadLocal myThreadLocal = new ThreadLocal() {
@Override
protected String initialValue() {
return "This is the initial value";
}
};
// 特别注意:
// 1. ThreadLocal实例 = 类中的private、static字段
// 2. 只需实例化对象一次 & 不需知道它是被哪个线程实例化
// 3. 每个线程都保持 对其线程局部变量副本 的隐式引用
// 4. 线程消失后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)
// 5. 虽然所有的线程都能访问到这个ThreadLocal实例,但是每个线程只能访问到自己通过调用ThreadLocal的set()设置的值
// 即 哪怕2个不同的线程在同一个`ThreadLocal`对象上设置了不同的值,他们仍然无法访问到对方的值
// 1. 设置值:set()
// 需要传入一个Object类型的参数
myThreadLocal.set("初始值”);
// 2. 读取ThreadLocal变量中的值:get()
// 返回一个Object对象
String threadLocalValue = (String) myThreadLocal.get();
实现原理:
TheradLocal 类中有一个map(ThreadLocalMap) : 用于存储每个线程 设置的存储在 ThreadLocal 变量的值
1、ThreadLocalMap的键 key = 当前ThreadLocal 实例、 值value = 该线程设置存储在ThreadLocal变量的值
2、该key 是ThreadLocal对象的弱引用,当要抛弃掉 ThreadLocal对象时,垃圾收集器会忽略该key 的引用二清理掉ThreadLoacl对象。
// ThreadLocal的源码
public class ThreadLocal {
...
/**
* 设置ThreadLocal变量引用的值
* ThreadLocal变量引用 指向 ThreadLocalMap对象,即设置ThreadLocalMap的值 = 该线程设置的存储在ThreadLocal变量的值
* ThreadLocalMap的键Key = 当前ThreadLocal实例
* ThreadLocalMap的值 = 该线程设置的存储在ThreadLocal变量的值
**/
public void set(T value) {
// 1. 获得当前线程
Thread t = Thread.currentThread();
// 2. 获取该线程的ThreadLocalMap对象 ->>分析1
ThreadLocalMap map = getMap(t);
// 3. 若该线程的ThreadLocalMap对象已存在,则替换该Map里的值;否则创建1个ThreadLocalMap对象
if (map != null)
map.set(this, value);// 替换
else
createMap(t, value);// 创建->>分析2
}
/**
* 获取ThreadLocal变量里的值
* 由于ThreadLocal变量引用 指向 ThreadLocalMap对象,即获取ThreadLocalMap对象的值 = 该线程设置的存储在ThreadLocal变量的值
**/
public T get() {
// 1. 获得当前线程
Thread t = Thread.currentThread();
// 2. 获取该线程的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
// 3. 若该线程的ThreadLocalMap对象已存在,则直接获取该Map里的值;否则则通过初始化函数创建1个ThreadLocalMap对象
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value; // 直接获取值
}
return setInitialValue(); // 初始化
}
/**
* 初始化ThreadLocal的值
**/
private T setInitialValue() {
T value = initialValue();
// 1. 获得当前线程
Thread t = Thread.currentThread();
// 2. 获取该线程的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
// 3. 若该线程的ThreadLocalMap对象已存在,则直接替换该值;否则则创建
if (map != null)
map.set(this, value); // 替换
else
createMap(t, value); // 创建->>分析2
return value;
}
/**
* 分析1:获取当前线程的threadLocals变量引用
**/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
/**
* 分析2:创建当前线程的ThreadLocalMap对象
**/
void createMap(Thread t, T firstValue) {
// 新创建1个ThreadLocalMap对象 放入到 Thread类的threadLocals变量引用中:
// a. ThreadLocalMap的键Key = 当前ThreadLocal实例
// b. ThreadLocalMap的值 = 该线程设置的存储在ThreadLocal变量的值
t.threadLocals = new ThreadLocalMap(this, firstValue);
// 即 threadLocals变量 属于 Thread类中 ->> 分析3
}
...
}
/**
* 分析3:Thread类 源码分析
**/
public class Thread implements Runnable {
...
ThreadLocal.ThreadLocalMap threadLocals = null;
// 即 Thread类持有threadLocals变量
// 线程类实例化后,每个线程对象拥有独立的threadLocals变量变量
// threadLocals变量在 ThreadLocal对象中 通过set() 或 get()进行操作
...
}
问题: ThreadLoacl 如何做到现成安全的
-
每个线程拥有自己独立的Threadlocals变量,也就是ThreadLocalMap
-
每当线程访问ThreadLocals变量时,访问的都是各自线程自己的 ThreadLocalMap变量(键值对)
-
ThreadLocalMap变量的键key = 唯一 = ThreadLocal 实例
问题:TheadLocal 与同步机制的区别
线程池 ThreadPool
定义: 一块缓存了一定线程数量的区域
作用:复用线程 管理线程(统一分配,监控, 控制线程池最大并发数)
优点:降低因线程的创建 销毁所带来的性能开销 - 可以重用缓存在线程池的线程。提高线程响应速度,执行效率。提高线程管力度
线程池有六个核心参数
// 创建线程池对象如下
// 通过 构造方法 配置核心参数
Executor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
sPoolWorkQueue,
sThreadFactory
);
// 构造函数源码分析
public ThreadPoolExecutor (int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue,
ThreadFactory threadFactory )
——————————————————————————————————————————————————————————————————————————————
// 1. 创建线程池
// 创建时,通过配置线程池的参数,从而实现自己所需的线程池
Executor threadPool = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
sPoolWorkQueue,
sThreadFactory
);
// 注:在Java中,已内置4种常见线程池,下面会详细说明
// 2. 向线程池提交任务:execute()
// 说明:传入 Runnable对象
threadPool.execute(new Runnable() {
@Override
public void run() {
... // 线程执行任务
}
});
// 3. 关闭线程池shutdown()
threadPool.shutdown();
// 关闭线程的原理
// a. 遍历线程池中的所有工作线程
// b. 逐个调用线程的interrupt()中断线程(注:无法响应中断的任务可能永远无法终止)
// 也可调用shutdownNow()关闭线程:threadPool.shutdownNow()
// 二者区别:
// shutdown:设置 线程池的状态 为 SHUTDOWN,然后中断所有没有正在执行任务的线程
// shutdownNow:设置 线程池的状态 为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
// 使用建议:一般调用shutdown()关闭线程池;若任务不一定要执行完,则调用shutdownNow()
根据不同参数的不同配置,java中最常见的线程池有四种