线程
操作系统调度最小单元,不可能无限制产生(受限的系统资源),线程创建与销毁有相应的开销。当系统中存在大量线程的时候,系统会通过时间片转轮的方式调度线程,因此线程不可能做到绝对运行。在进程中避免频繁创建销毁所带来的系统开销,就应该使用线程池。线程池会缓存一定数量的线程,Andoid线程池来自于java,主要通过Executor派生特定类型的线程池
注释:
时间片转轮调度算法:每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。调度程序所要做的就是维护一张就绪进程列表,当进程用完它的时间片后,它被移到队列的末尾。简单地说,线程组(ThreadGroup)就是由线程组成的管理线程的类。
特殊名词提前解释
ThreadGroup(线程组):
线程组ThreadGroup表示一组线程的集合,一旦一个线程归属到一个线程组之中后,就不能再更换其所在的线程组。那么为什么要使用线程组呢?个人认为有以下的好处:方便统一管理,线程组可以进行复制,快速定位到一个线程,统一进行异常设置等。ThreadGroup它其实并不属于Java并发包中的内容,它是java.lang中的内容。
线程组和线程池的区别:
二者最直观的区别是,线程池就像一台水轮机,它的叶片就像线程,可以循环不停,看起来只要水源不断,叶片就可以不停的周期性的工作。而线程组,只是管理一个或多个线程的Manager,而且它所管理的线程生命周期一旦结束,将永远停止,无法周期执行任务.
Future接口
Future接口代表异步计算的结果,通过Future接口提供的方法可以查看异步计算是否执行完成,或者等待执行结果并获取执行结果,同时还可以取消执行。
FutureTask
Future只是一个接口,不能直接用来创建对象,FutureTask是Future的实现类
FutureTask实现了RunnableFuture接口,则RunnableFuture接口继承了Runnable接口和Future接口,所以FutureTask既能当做一个Runnable直接被Thread执行,也能作为Future用来得到Callable的计算结果。
扮演线程角色的有:Thread,AsyncTask,IntentService,HandlerThread。(建议到此先看线程池)
AsyncTask
轻量级异步任务类(不适合特别耗时的任务),线程池执行后台任务,然后把进度和最终结果传给主线程更新UI
封装了线程池和 Handler ,主要是为了方便更新UI,由于多次改版原因,在不同api版本上有不同的表现
-
THREAD_POOL_EXECUTOR:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = CPU_COUNT + 1; private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE = 1; public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
- 核心线程数 = CPU核心数 + 1
- 最大线程数为CPU核心数的两倍 + 1
- 核心线程无超时机制
- 任务队列容量为128
-
源码解析笔记:
1 . 源码中线程池有两个线程池(SerialExecutor 和 THREAD_POOL_EXECUTOR)和一个 Handler(InternalHandler),SerialExecutor 用于任务的排队,而THREAD_POOL_EXECUTOR用于真正地执行任务,InternalHandler用于将执行环境从线程池切换到主线程
2 . AsyncTask 534行 execute方法:sDefaultExecutor为串行的线程池
3 . AsyncTask 645行的finish方法为 628 行的finish方法,finish() 中调用了onPostExecute(Result result) 这个空方法
4 . executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"")与execute(""),前者为并发,后者为串行,由 AsyncTask 源码590行exec.execute(mFuture);可知当线程池为默认线程池的时候是SerialExecutor implements Executor ,默认为串行,SerialExecutor真正的任务会一个一个的交给 THREAD_POOL_EXECUTOR 执行,而外层直接调用executeOnExecutor时传入THREAD_POOL_EXECUTOR ,那么任务就直接交给了THREAD_POOL_EXECUTOR 去执行(此线程池内部为并发)
HandlerThread
内部封装有looper的线程(上次分享会已讲)
IntentService
IntentService是一种服务,内部封装有HandlerThread和Handler,当任务执行完后IntentService会自动退出,作用很像一个后台线程,但是因为它是四大组件之一,所以进程优先级比线程高,不易被杀死,所以适用于高优先级的后台任务
startService(new Intent(this, LocalIntentService.class));就可以启动Service了(LocalIntentService.class为继承IntentService的实现类)。
在LocalIntentService类中,onHandleIntent()为处理Intent的方法。
源码解析 startService ->... -> onHandleIntent():
startService() 方法为Context 的一个方法,调用 startService() 后,系统会回调 Service 类的 onCreate() 以及 onStart() 方法。这样启动的 Service 会一直运行在后台,直到 Context.stopService() 或者 selfStop() 方法被调用。另外如果一个 Service 已经被启动,其他代码再试图调用 startService() 方法,是不会执行 onCreate() 的,但会重新执行一次 onStart() 。
onCreate() :先创建一个 HandlerThread 对象,然后使用他的 looper 来构造一个Handler 对象 mServiceHandler(这样通过 mServiceHandler 发送的消息都会在IntentService 中执行)。此时更像是完成了初始化工作
onStartCommand():每次启动 IntentService,他的 onStartCommand 方法就会调用一次,而 onStartCommand方法里面又每次都会调用 onStart,注意这个方法不要重写
onStart() :这个方法将 Intent 封装成了一个Message,并通过 mServiceHandler 发送到 HandlerThread 中处理。注意 mServiceHandler 绑定的 looper 就是HandlerThread 的looper,所以最后会调用 mServiceHandler 的 handleMessage(Message msg) 处理后台任务了。
-
ServiceHandler 类:
@Override public void handleMessage(Message msg) { onHandleIntent((Intent)msg.obj); stopSelf(msg.arg1); }
最主要的就是这两行:可以看见 onHandleIntent((Intent)msg.obj) 把传递到这里的交给了我们最外层看见的 onHandlerIntent()。同时也说明按顺序处理消息(因为 Handler 中的 looper 是按顺序执行后台任务)
- onHandlerIntent 执行完毕后,源码中通过 stopSelf(int startId)尝试停止服务
stopSelf() 会直接停止服务,而 stopSelf(int startId)会等消息处理完才停止
线程池
真正的线程池的实现为ThreadPoolExecutor。ThreadPoolExecutor提供一系列参数配置线程池。
线程池的好处:
- 重用,节省性能开销
- 控制线程池并发数,避免大量线程相互抢占系统资源而导致的阻塞现象
- 能进行简单管理,提供定时执行及指定间隔循环执行等功能
专业名词提前解释(构造函数中的主要参数)
- corePoolSize
- maxmumPoolSize
- keepAliveTime
- unit:keepAliveTime 的时间单位
- workQueue:线程池任务队列,通过线程池的execute 方法提交的 Runnable 对象会储存在这个参数中
- threadFactory:线程工厂,为线程池提供创建新线程的功能。为接口。
线程ThreadPoolExecutor源码简单介绍
核心线程:
Executor与Executors介绍
Android的线程池主要分为四种:
- FixedThreadPool
- CachedThreadPool
- ScheduledThreadPool
- SingleThreadPool
FixedThreadPool :
能复用 固定数量的线程 处理一个 共享的无边界队列 。线程处于空闲时不会被收回。如果当所有线程都是活动时,有多的任务被提交过来,那么它会一致在队列中等待直到有线程可用。如果任何线程在执行过程中因为错误而中止,新的线程会替代它的位置来执行后续的任务。所有线程都会一致存于线程池中,直到显式的执行 ExecutorService.shutdown() 关闭。
线程池中只有核心线程且不会被收回,意味着能快速响应外界请求
此核心线程没有超时机制
CachedThreadPool:
CachedThreadPool 是通过 java.util.concurrent.Executors 创建的 ThreadPoolExecutor 实例。这个实例会根据需要,在线程可用时,重用之前构造好的池中线程。这个线程池在执行 大量短生命周期的异步任务时(many short-lived asynchronous task),可以显著提高程序性能。调用 execute 时,可以重用之前已构造的可用线程,如果不存在可用线程,那么会重新创建一个新的线程并将其加入到线程池中。如果线程超过 60 秒还未被使用,就会被中止并从缓存中移除。因此,线程池在长时间空闲后不会消耗任何资源。
核心线程数为0
适用于大量耗时操作
ScheduledThreadPool:
核心线程数量固定,非核心线程数量没有限制,当非核心线程闲置时会被立即收回。
ScheduledThreadPool主要用于执行定时任务和具体固定周期的重复任务
SingleThreadPool:
线程池内部只有一个核心线程,确保所有任务都在一个线程中按顺序执行。和 FixedThreadPool的区别在于,如果线程遇到错误中止,它是无法使用替代线程的。
统一所有外界任务到一个线程中,使这些任务不需要处理同步问题
四种线程池的实现及实现源码
Executors -> ... ->ThreadPoolExecutor