线程和线程池

android沿用了java的线程模式,其中线程也分主线程和子线程。

一、线程

1、主线程和子线程

一个程序在运行,也就是一个进程在执行,一个进程里面可以有很多线程。

  • 主线程:也叫UI线程,用于运行四大组件和用户之间的交互。主线程是指进程所拥有的线程,一个进程只有一个主线程。
  • 子线程:一般使用子线程来进行耗时操作,比如IO操作或者网络请求(安卓4.0以后的网络请求是不允许在主线程中执行的,只能放在子线程),更新UI的操作放在主线程,安卓中的子线程不允许更新UI。

2、安卓中谁是线程?

在安卓中,线程主要有以下四个:

  • Thread:最基本的线程;
  • AsyncTask:封装了线程池和Handler;
  • HandlerThread:内部封装了looper对象的一个线程类,可以进行消息的循环;
  • IntentService:内部封装了HandlerThread和Handler,在onHandleIntent()方法中来执行异步任务,任务结束后自动退出。

二、线程池

1、线程的产生和销毁都会消耗性能

在安卓中,线程是程序可以执行的最小单元,线程不能无限创建,因为创建线程和销毁线程都会造成性能开销。

当系统中有大量的线程时,系统会让时间片轮转的方式来调度每一个线程,但是当线程数量大于CPU的核心数时(一般都是大于),就不一定能保证每一个线程都能得到绝对的并行,但是又不可能在进程中大量的创建和销毁线程,这个时候就需要一个工具来管理线程,这就是线程池。

线程池的基本思想是对象池的思想,开辟一块内存空间,里面存放了众多的线程,池中线程执行调度由线程池管理器来处理。当有线程任务时,从池中取出一个,执行完成后线程对象归池,这样就可以避免反复开辟线程,销毁线程带来的性能消耗,节省了系统资源。

比如:一个应用要和网络打交道,有很多步骤需要访问网络,为了不阻塞主线程,每个步骤都创建个线程,在线程中和网络交互,用线程池就变的简单,线程池是对线程的一种封装,让线程用起来更加简便,只需要创一个线程池,把这些步骤像任务一样放进线程池,在程序销毁时只要调用线程池的销毁函数即可。

2、使用线程池的好处

  • 重用线程,避免重复创建和销毁线程带来的性能开销;
  • 可以控制线程最大并发数,避免大量的进程抢夺系统资源,导致界面卡顿;
  • 可以定时执行。

3、Executor和ThreadPoolExecutor

安卓中线程池的概念来自于java中的Executor,它是一个接口,ThreadPoolExecutor是它的具体实现类。ThreadPoolExecutor通过一系列的参数来配置线程池,安卓中的线程池一共有四种,后面会说到,因为安卓中的线程池都是直接或者间接的配置ThreadPoolExecutor来实现的,所以我们有必要了解一下ThreadPoolExecutor。

4、ThreadPoolExecutor

ThreadPoolExecutor有好多构造方法,通过研究它的构造方法,我们就可以了解它怎么用。

  • 最常用的构造方法:
public ThreadPoolExecutor(int corePoolSize,
                            int maximumPoolSize,
                            long keepAliveTime,
                            TimeUnit unit,
                            BlockingQueue<Runnable> workQueue,
                            ThreadFactory threadFactory) {
      this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
           threadFactory, defaultHandler);
  }
  • 最长的构造方法:
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {}

因为最长,所以最全面,那么我们就来研究一下这个构造方法吧。

  • corePoolSize:线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使它们处于闲置状态。
    • 如果将executor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程在等待新任务到来时会有超时策略(超时判断),如果在keepAliveTime指定的时间之外还处于等待状态的话,核心线程也会被终止(所以核心线程也不是可以毫无条件地存活下去的);
  • maximumPoolSize:线程池所能容纳的最大线程数,当活动线程达到这个数值之后,后续的新任务将会被阻塞在外面。
  • keepAliveTime 非核心线程闲置的超时时长,如果超过了这个时长,非核心线程就会被回收。
    • 当 ThreadPoolExecutor 的allowCoreThreadTimeOut属性被设置为true时,keepAliveTime也会作用于核心线程
  • unit:用于指定 keepAliveTime的参数的时间单位,这是一个枚举,常用的有 TimeUnit.MILLISECONDS (毫秒),TimeUnit.SECONDS (秒),TimeUnit.MINUTES (分钟)等
  • workQueue 线程池中的任务队列,通过线程池的 execute 方法提交的 Runnable 对象会存储在这个参数中。
  • threadFactort 线程工厂,为线程池提供创建新线程的功能,ThreadFactory是一个接口,它只有一个方法,Thread newThread(Runnable r)
  • RejectedExecutionHandler(不常用) 当线程池无法执行新任务时,(任务队列已满或者其他原因),那么 rejectedExecutio会抛出一个异常给调用者。

5、线程池的运行机制

一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。

当一个任务通过execute(Runnable)方法想要添加到线程池时:

  • 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理任务,直到保证线程池中的核心线程数够数了为止;
  • 如果此时线程池中的数量等于corePoolSize,但是任务队列workQueue未满,那么这个任务会被加入到任务队列中;
  • 如果此时线程池中的数量大于corePoolSize,任务队列workQueue也满了,但是线程的数量小于maximumPoolSize,那么就新建线程来处理任务;
  • 如果此时线程池中的数量大于corePoolSize,任务队列workQueue也满了,但是线程的数量等于maximumPoolSize,就通过Handler所指定的策略来处理任务。

总结一下:处理任务的优先级如下:
核心线程corePoolSize>任务队列workQueue>最大线程数maximumPoolSize,如果都满了,就是用handler来处理。

如果线程池中的线程数量大于corePoolSize,但是他们的空闲时间超过了keepAliveTime,线程会被终结,这样,线程池就可以动态的调整池中的线程数量。

这里写图片描述

三、线程池的分类

刚才也说了,安卓中一共有四种线程池,它们都直接或者间接的配置ThreadPoolExecutor,那么是哪四种呢?

  • FixedThreadPool
  • CacheThreadPool
  • SchedeledThreadPool
  • SingleThreadExecutor

1、FixedThreadPool

  • 创建:FixedThreadPool通过newFixedThreadPool()方法来创建,它的核心线程数corePoolSize等于maximumPoolSize
  • 特点:
    • 线程池里面的线程固定,只有核心线程,除非线程池被关闭,否则,核心线程不会被回收;
    • 没有超时机制
    • 当线程池里面的线程都处于活跃状态时,新的任务会处于等待状态,直到有线程空出来;
    • 任务队列的大小没有限制。

newFixedThreadPool 方法在 java.util.concurrent.Executors 里面:

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
         return new ThreadPoolExecutor(nThreads, nThreads,
                                       0L, TimeUnit.MILLISECONDS,
                                       new LinkedBlockingQueue<Runnable>(),
                                       threadFactory);
     }

2、CacheThreadPool

  • 创建:CacheThreadPool通过newCacheThreadPool()方法来创建
  • 特点:
    • 线程池的核心线程数量不定,只有非核心线程;
    • 最大线程数为Integer.MAX_VALUE,也就是说无限多;
    • 当线程池中线程都处于活跃状态,有新的任务到来时,会创建新的线程来处理;
    • 有超时机制,超过60秒,闲置线程就会被回收
  • 适用场景:适用于任务较多但都耗时较少的情况,当整个线程池都处于闲置状态时,线程池中的任务会因为超时而停止,所以它们几乎是不占系统资源的。
public static ExecutorService newFixedThreadPool(int nThreads) {
         return new ThreadPoolExecutor(nThreads, nThreads,
                                       0L, TimeUnit.MILLISECONDS,
                                       new LinkedBlockingQueue<Runnable>());
     }

3、ScheduledThreadPool

  • 创建:通过newScheduledThreadPool()方法来创建;
  • 特点:
    • 核心线程数固定,非核心线程数不固定;
    • 非核心线程闲置时会被回收;
  • 使用场景:执行定时任务和具有固定周期的重复任务时
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
       return new ScheduledThreadPoolExecutor(corePoolSize);
    }

4、SingleThreadExecutor

  • 创建:通过newSingleThreadExecutor()方法来创建
  • 特点:
    • 只有一个核心线程,确保所有的任务都在一个线程中顺序执行;
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

4种线程池的简单演示:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
        fixedThreadPool.execute(command);

        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        cachedThreadPool.execute(command);

        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);
        // 2000ms后执行command
        scheduledThreadPool.schedule(command, 2000, TimeUnit.MILLISECONDS);
        // 延迟10ms后,每隔1000ms执行一次command
        scheduledThreadPool.scheduleAtFixedRate(command, 10, 1000, TimeUnit.MILLISECONDS);

        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        singleThreadExecutor.execute(command);

好了,到这里,关于线程池的知识就结束了。

总结

线程是程序运行的最小单元,线程不能无限创建,线程的创建和销毁都会对系统性能造成消耗,所以需要使用线程池来管理线程。线程池有四种,分别是FixedThreadPool、CacheThreadPool、ScheduledThreadPool、SingleThreadExecutor,每一种中核心线程数、非核心线程数、超时机制等都不一样。还有就是线程池中任务处理的优先级:核心线程coreThreadSize>任务队列work queue>最大线程数量>maximumPoolSize>Handler。

好了,这一篇就到这里结束了,下一篇我们来重点讲一下安卓中的多线程。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值