关于多线程

2017-08-10  本文已影响0人  黑石ZB

线程运行机制

thread1.png

线程池

Android中耗时的操作,都会开子线程,线程的创建和销毁是要消耗系统资源的。为了减少频繁的线程的创建和销毁带来的不必要的开销,可以使用线程池。 线程池的优点:

线程池的应用非常广泛,在众多的开源框架中也总能看到线程池的踪影。

线程池涉及的类

task.png

ThreadPoolExecutor介绍

//构造方法
public ThreadPoolExecutor(int corePoolSize,//核心池的大小
                              int maximumPoolSize,//线程池最大线程数
                              long keepAliveTime,//保持时间
                              TimeUnit unit,//时间单位
                              BlockingQueue<Runnable> workQueue,//任务队列
                              ThreadFactory threadFactory,//线程工厂
                              RejectedExecutionHandler handler) //异常的捕捉器

构造相关参数解释

TimeUnit.DAYS;               //天
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒
ArrayBlockingQueue;  //有界队列
LinkedBlockingQueue;  //无界队列
PriorityBlockingQueue;  //优先级队列
SynchronousQueue;  //交替队列
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 

基础API的介绍

任务提交给线程池之后的处理策略(重要)

  1. 如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建新的线程执行这个任务;
task1.png
  1. 如果当前线程池中的线程数目等于corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中
    • 若添加成功,则该任务会等待空闲线程将其取出去执行;
task2.png task3.png
  1. 如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;
task4.png
  1. 如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。

阻塞队列的介绍(BlockingQueue)

阻塞队列,如果BlockingQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒,同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间时才会被唤醒继续操作。

  1. 基础API介绍

    • 往队列中加元素的方法

      • add(E) : 非阻塞方法, 把元素加到BlockingQueue里,如果BlockingQueue可以容纳,则返回true,否则抛出异常。
      • offer(E) : 非阻塞, 表示如果可能的话,将元素加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false。
      • put(E):阻塞方法, 把元素加到BlockingQueue里,如果BlockingQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里有空间再继续。
    • 从队列中取元素的方法

      • poll(time): 阻塞方法,取走BlockingQueue里排在首位的元素,若不能立即取出,则可以等time参数规定的时间,取不到时返回null。
      • take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的对象被加入为止。
  2. 子类介绍

    • ArrayBlockingQueue(有界队列): FIFO 队列,规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小

    • LinkedBlockingQueue(无界队列):FIFO 队列,大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定。

    • PriorityBlockingQueue:优先级队列, 类似于LinkedBlockingQueue,但队列中元素非 FIFO, 依据对象的自然排序顺序或者是构造函数所带的Comparator决定的顺序

    • SynchronousQueue(直接提交策略): 交替队列,队列中操作时必须是先放进去,接着取出来,交替着去处理元素的添加和移除,这是一个很有意思的阻塞队列,其中每个插入操作必须等待另一个线程的移除操作,同样任何一个移除操作都等待另一个线程的插入操作。因此此队列内部其 实没有任何一个元素,或者说容量是0,严格说并不是一种容器。由于队列没有容量,因此不能调用peek操作,因为只有移除元素时才有元素。

RejectedExecutionHandler介绍

实现的子类介绍

Executors

帮助我们方便的生成一些常用的线程池,ThreadPoolExecutor是Executors类的底层实现

newSingleThreadExecutor(单线程池)

创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池可以保证所有任务的执行顺序按照任务的提交顺序执行。

newFixedThreadPool (固定线程池)

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

newCachedThreadPool(缓存线程池)

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

newScheduledThreadPool(无限线程池)

创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

线程池的创建

private ThreadPoolExecutor mThreadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);

线程池中执行Runnable

mThreadPoolExecutor.execute(downloadTask);

取消下载

private void cancelDownload(DownloadInfo downloadInfo) {
    ThreadPoolProxy.getInstance().remove(downloadInfo.getDownloadTask());
    downloadInfo.setDownloadStatus(STATE_UN_DOWNLOAD);
    notifyObservers(downloadInfo);
}
上一篇 下一篇

猜你喜欢

热点阅读