准备最近多读一读其他人的源码,沉淀一些东西,同时补充下自己知识上的遗漏之处。
Android-Lite-Go
第一篇文章的源码来自于@马天宇大神的开源社区的最新的项目LiteGo,是一款基于Java的迷你异步并发类库。之所以选择它,因为它真的很迷你,也比较简单。
在很早直接进行Android开发的时候,实现异步/多线程,常常是简单的new Thread(),然后就完事了。到之后渐渐的开始学会到ThreadPool来用线程池管理异步操作。在看完这个库之后,才进一步明白需要在项目里统一一个线程池进行管理,同时根据业务类型设置不同的策略。
处理异步并发的理念
- 清闲时线程不要多持,最好不要超过CPU数量,根据具体应用类型和场景来决策。
- 瞬间并发不要过多,最好保持在CPU数量左右,或者可以多几个问题并不大。
- 注意控制排队和满载策略,大量并发瞬间起来的场景下也能轻松应对。
- 同时并发的线程数量不要过多,最好保持在CPU核数左右,过多了CPU时间片过多的轮转分配造成吞吐量降低,过少了不能充分利用CPU,并发数可以适当比CPU核数多一点没问题。
- 业务上合理调度任务,优化业务逻辑,从自己做起,不胡搞乱搞咯。
开始看源码
OverloadPolicy
枚举类型,定义了5个线程池过载的策略。分别是:DiscardNewTaskInQueue(丢弃队列里最新的任务)、DiscardOldTaskInQueue(丢弃队列里最老的任务)、DiscardCurrentTask(丢弃当前的任务(--没用到))、
CallerRuns(直接执行,同时打印出log)、
ThrowExecption(抛出异常)
SchedulePolicy
枚举类型,定义了队列的执行策略。分布是:
LastInFirstRun、FirstInFistRun
PriorityRunnable
实现了Runnable接口的抽象类,里面有一个优先级的属性,不过没有用到。
GoUtil
工具类。主要用于获取CPU的核心数以及显示提示信息弹窗。
SmartExecutor
实现了Executor接口,核心组件,它可以用来作为「App」内支持异步并发的唯一组件。 在一个App中「SmartExecutor」可以有多个实例,每个实例都有完全的「独立性」,比如独立的「核心并发」、「排队等待」指标,独立的「运行调度和满载处理」策略,但所有实例「共享一个线程池」。 这种机制既满足不同模块对线程控制和任务调度的独立需求,又共享一个池资源来节省开销,最大程度上节约资源复用线程,帮助提升性能。
比较重要的属性:
static ThreadPoolExecutor threadPool:静态的线程池,保证一个APP持有唯一的一个线程池。
coreSize:CPU的核心数,也决定了并发的最大数量。
queueSize:等于32*coreSize,也可以自己设置,决定了同时运行任务满载后等待队列的长度。
final Object lock:加锁防止并发抢夺资源,synchronized(lock)。
runningList:执行中的任务,封装成了类似『链表』的结构体。
waitingList:等待任务表,同上。
拓展学习:
(1)关于synchronized
参考链接
通常在多线程的环境下,为了防止并发抢夺资源,Java中使用synchronized来实现加同步锁。它包括两种用法:synchronized 方法和 synchronized 块。
synchronized锁住的是对象,而不是代码。
- 在个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
- 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
- 然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
- 尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
- 所以我们在用synchronized关键字的时候,能缩小代码段的范围就尽量缩小,能在代码段上加同步就不要再整个方法上加同步。这叫减小锁的粒度,使代码更大程度的并发。原因是基于以上的思想,锁的代码段太长了,别的线程就要等待很久。
- 可以使用synchronized (Test.class)实现全局锁的效果。
- static synchronized方法也相当于全局锁。
-
synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;
synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的其他代码是可以被多个线程同时访问到的。 - synchronized块写法:
synchronized(object)
{
}
表示线程在执行的时候会将object对象上锁。(注意这个对象可以是任意类的对象,也可以使用this关键字)。 - 在使用synchronized块时应注意,synchronized块只能使用对象作为它的参数。如果是简单类型的变量(如int、char、boolean等),不能使用synchronized来同步。
根据以上的内容,我们发现源码中的synchronized是对象锁.看到这里有一个疑问,如果源码中使用的是对象锁,SmartExecutor在APP使用中是会创建不同对象的,那这些锁是为了起到什么样的作用呢?让我们继续看下去。