多线程的介绍
介绍之前,首先需要明白两个概念,android当中的并行和并发指的是什么?
并发:在系统的一个时间片内,所有任务都在交替的工作,但一个时刻只有一个任务在运行。
并行:在系统的一个时刻,所有任务都在同时进行。
tip:可以看出,并发指的是单核上,通过任务频繁切换来实现;而并行指的是依赖多核处理器同时工作。
定义
多线程顾名思义就是开辟多个线程进行工作,换而言之多个任务“同时”进行。
“同时”,这个主要归咎于JVM虚拟机快速调度来轮换线程,使得多个线程能够轮流快速的执行,从而给我们一种同时进行的错觉。
作用
多线程的作用是使程序在并发过程中,减少多个任务在等待线程处理所带来的时间的损耗。
官方声明在多线程编程时有两大原则:
- 不要阻塞UI线程:主线程被阻塞超过5s,则会出现ANR的异常,故主线程不能进行耗时的操作。
- 不要在UI线程之外的线程进行UI操作。
使用方式
线程
线程的使用方式主要分为三大类:基础使用,复合使用,线程池。
-
基础使用
继承Thread类,或者匿名内部类创建一个Thread对象。val thread = Thread { } thread.start()
实现Runnable接口,由于是接口,因此不会影响本身类的继承。
class MyRunnable: Runable{ override fun run() { } } val thread = Thread(new MyRunnable(), "Thread-1")
实现Callable接口,相比Runnable,这种实现方式具有返回值,是个泛型,同时能抛出异常。
class MyCallable: Callable<Integer>{ @throws override fun call():Integer{ } } val futrue: Future<Integer> = Excutors.newCacheThreadPool(1).submit(new MyCallable())
-
复合使用
AsyncTask:异步任务,现版本已经被废弃了,由于它会容易持有外部Activity或Fragment引用,在后台运行,即是Activity被销毁,导致也不能够释放资源,引发泄漏问题;并且生命周期维护复杂,在屏幕旋转等操作,管理任务不好处理。
HandlerThread:Android提供的子线程,一个带有looper的线程,用于处理后台任务。
IntentService:现版本已经废弃,被强大的WorkManager代替。传入的intent会被加入到一个单独的工作线程中处理,通过维护HandlerThread来实现的,任务完成,会自动结束服务。
线程池
即使多线程的使用,能够避免类似单线程执行多任务时,所付出的时空的消耗,但是盲目的开辟线程会造成大量的内存资源的浪费。这个时候就需要引入线程池的使用。
创建线程是需要依赖系统底层的调度实现的;但是维护一个线程池,通过线程池进行线程间的调度,是应用层实现的。
kotlin的协程就是这个原理
作用:对线程的复用,避免资源竞争出现资源浪费的问题。
常见的线程池类型:FixThreadPool、CacheThreadPool、SingleThreadPool、ScheduledThreadPool
这些线程池由Executor类提供,不同场景于创建不同类型的线程池。
- FixedThreadPool:适用于需要限制并发线程数量的场景,负载较为稳定。
- CachedThreadPool:适用于执行大量短期异步任务的场景,负载较为突发。
- SingleThreadExecutor:适用于需要保证任务顺序执行的场景。
- ScheduledThreadPool:适用于需要定时或周期性执行任务的场景。
自定义线程池
public ThreadPoolExcutor(int corePoolSize
int maximumPoolSize
long keepAliveTime
TimeUnit unit
BlockingQueue<Runnable> workQueue
ThreadFactory threadFactory
RejectedExecutionHandler handler)
1)corePoolSize:核心线程数
2)maximumPoolSize:最大线程数
3)keepAliveTime:非核心线程保活时间
4)unit:时间单元
5)workQueue:工作队列
6)threadFactory:线程工厂
7)handler:拒绝策略