一文彻底了解Android中的线程和线程池

目录
  • 前言
  • 1.主线程和子线程
  • 2.Android中的线程形态
    • 2.1 AsyncTask
    • 2.2 AsyncTask的工作原理
    • 2.3 HandleThread
    • 2.4 IntentService
  • 3.Android中的线程池
    • 3.1 ThreadPoolExecutor
    • 3.2线程池的分类
  • 总结

前言

从用途上来说Android的线程主要分为主线程和子线程两类,主线程主要处理和界面相关的工作,子线程主要处理耗时操作。除Thread之外,Android中还有其他扮演线程的角色如AsyncTask、IntentService、HandleThread,其中AsyncTask的底层用到了线程池,IntentService和HandleThread的底层直接使用了线程。

AsyncTask内部封装了线程池和Handler主要是为了方便开发者在在线程中更新UI;HandlerThread是一个具有消息循环的线程,它的内部可以使用Handler;IntentService是一个服务,系统对其进行了封装使其可以更方便的执行后台任务,IntentService内部采用HandleThread来执行任务,当任务执行完毕后IntentService会自动退出。IntentService是一个服务但是它不容易被系统杀死因此它可以尽量的保证任务的执行。

1.主线程和子线程

主线程是指进程所拥有的的线程,在Java中默认情况下一个进程只能有一个线程,这个线程就是主线程。主线程主要处理界面交互的相关逻辑,因为界面随时都有可能更新因此在主线程不能做耗时操作,否则界面就会出现卡顿的现象。主线程之外的线程都是子线程,也叫做工作线程。

Android沿用了Java的线程模型,也有主线程和子线程之分,主线程主要工作是运行四大组件及处理他们和用户的交互,子线程的主要工作就是处理耗时任务,例如网络请求,I/O操作等。Android3.0开始系统要求网络访问必须在子线程中进行否则就会报错,NetWorkOnMainThreadException

2.Android中的线程形态

2.1 AsyncTask

AsyncTask是一个轻量级的异步任务类,它可以在线程池中执行异步任务然后把执行进度和执行结果传递给主线程并在主线程更新UI。从实现上来说AsyncTask封装了Thread和Handler,通过AsyncTask可以很方便的执行后台任务以及主线程中访问UI,但是AsyncTask不适合处理耗时任务,耗时任务还是要交给线程池执行。

AsyncTask的四个核心类如下:

    • onPreExecute():主要用于做一些准备工作,在主线程中执行异步任务执行之前
    • doInBackground(Params ... params):在线程池执行,此方法用于执行异步任务,params表示输入的参数,在此方法中可以通过publishProgress方法来更新任务进度,publishProgress会调用onProgressUpdate
    • onProgressUpdate(Progress .. value):在主线程执行,当任务执行进度发生改变时会调用这个方法
    • onPostExecute(Result result):在主线程执行,异步任务之后执行这个方法,result参数是返回值,即doInBackground的返回值。

2.2 AsyncTask的工作原理

2.3 HandleThread

HandleThread继承自Thread,它是一种可以使用Handler的Thread,它的实现在run方法中调用Looper.prepare()来创建消息队列然后通过Looper.loop()来开启消息循环,这样在实际使用中就可以在HandleThread中创建Handler了。

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

HandleThread和Thread的区别是什么?

    • Thread的run方法中主要是用来执行一个耗时任务;
    • HandleThread在内部创建了一个消息队列需要通过Handler的消息方式来通知HandleThread执行一个具体的任务,HandlerThread的run方法是一个无限循环因此在不使用是调用quit或者quitSafely方法终止线程的执行。HandleTread的具体使用场景是IntentService。

2.4 IntentService

IntentService继承自Service并且是一个抽象的类因此使用它时就必须创建它的子类,IntentService可用于执行后台耗时的任务,当任务执行完毕后就会自动停止。IntentService是一个服务因此它的优先级要比线程高并且不容易被系统杀死,因此可以利用这个特点执行一些高优先级的后台任务,它的实现主要是HandlerThread和Handler,这点可以从onCreate方法中了解。

//IntentService#onCreate
@Override
public void onCreate() {
    // TODO: It would be nice to have an option to hold a partial wakelock
    // during processing, and to have a static startService(Context, Intent)
    // method that would launch the service & hand off a wakelock.

    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
}

当IntentService第一次被启动时回调用onCreate方法,在onCreate方法中会创建HandlerThread,然后使用它的Looper创建一个Handler对象ServiceHandler,这样通过mServiceHandler把消息发送到HandlerThread中执行。每次启动IntentService都会调用onStartCommand,IntentService在onStartCommand中会处理每个后台任务的Intent。

//IntentService#onStartCommand
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

//IntentService#onStart
@Override
public void onStart(@Nullable Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        onHandleIntent((Intent)msg.obj);
        stopSelf(msg.arg1);
    }
}

onStartCommand是如何处理外界的Intent的?

在onStartCommand方法中进入了onStart方法,在这个方法中IntentService通过mserviceHandler发送了一条消息,然后这个消息会在HandlerThread中被处理。mServiceHandler接收到消息后会把intent传递给onHandlerIntent(),这个intent跟启动IntentService时的startService中的intent是一样的,因此可以通过这个intent解析出启动IntentService传递的参数是什么然后通过这些参数就可以区分具体的后台任务,这样onHandleIntent就可以对不同的后台任务做处理了。当onHandleIntent方法执行结束后IntentService就会通过stopSelf(int startId)方法来尝试停止服务,这里不用stopSelf()的原因是因为这个方法被调用之后会立即停止服务但是这个时候可能还有其他消息未处理完毕,而采用stopSelf(int startId)方法则会等待所有消息都处理完毕后才会终止服务。调用stopSelf(int startId)终止服务时会根据startId判断最近启动的服务的startId是否相等,相等则立即终止服务否则不终止服务。

每执行一个后台任务就会启动一次intentService,而IntentService内部则通过消息的方式向HandlerThread请求执行任务,Handler中的Looper是顺序处理消息的,这就意味着IntentService也是顺序执行后台任务的,当有多个后台任务同时存在时这些后台任务会按照外界发起的顺序排队执行。

3.Android中的线程池

线程池的优点:

    • 线程池中的线程可重复使用,避免因为线程的创建和销毁带来的性能开销;
    • 能有效控制线程池中的最大并发数避免大量的线程之间因互相抢占系统资源导致的阻塞现象;
    • 能够对线程进行简单的管理并提供定时执行以及指定间隔循环执行等功能。

Android的线程池的概念来自于Java中的Executor,Executor是一个接口,真正的线程的实现是ThreadPoolExecutor,它提供了一些列参数来配置线程池,通过不同的参数可以创建不同的线程池。

3.1 ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

ThreadPoolExecutor是线程池的真正实现,它的构造函数中提供了一系列参数,先看一下每个参数的含义:

    • corePoolSize:线程池的核心线程数,默认情况下核心线程会在线程池中一直存活即使他们处于闲置状态。如果将ThreadPoolExecutor的allowCoreThreadTimeOut置为true那么闲置的核心线程在等待新的任务到来时会有超时策略,超时时间由keepAliveTime指定,当等待时间超过keepAliveTime设置的时间后核心线程就会被终止。
    • maxinumPoolSize:线程池中所能容纳的最大线程数,当活动线程达到做大数量时后续的新任务就会被阻塞。
    • keepAliveTime:非核心线程闲置时的超时时长,超过这个时长非核心线程就会被回收。
    • unit:用于指定超时时间的单位,常用单位有毫秒、秒、分钟等。
    • workQueue:线程池中的任务队列,通过线程池中的execute方法提交的Runnable对象会存储在这个参数中。
    • threadFactory:线程工厂,为线程池提供创建新的线程的功能。
    • handler:这个参数不常用,当线程池无法执行新的任务时,这可能是由于任务队列已满或者无法成功执行任务,这个时候ThreadPoolExecutor会调用handler的rejectExecution方法来通知调用者。

ThreadPoolExecutor执行任务时大致遵循如下规则:

  • 如果线程池中的线程数量没有达到核心线程的数量那么会直接启动一个核心线程来执行任务;

    如果线程池中线程数量已经达到或者超过核心线程的数量那么会把后续的任务插入到队列中等待执行;

    如果任务队列也无法插入那么在基本可以确定是队列已满这时如果线程池中的线程数量没有达到最大值就会立刻创建非核心线程来执行任务;

    如果非核心线程的创建已经达到或者超过线程池的最大数量那么就拒绝执行此任务,同时ThreadPoolExecutor会通过RejectedExecutionHandler抛出异常rejectedExecution。

3.2线程池的分类

  • FixedThreadPool:它是一种数量固定的线程池,当线程处于空闲状态时也不会被回收,除非线程池被关闭。当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有空闲线程出来。FixedThreadPool只有核心线程并且不会被回收因此它可以更加快速的响应外界的请求。
  • CacheThreadPool:它是一种线程数量不定的线程池且只有非核心线程,线程的最大数量是Integer.MAX_VALUE,当线程池中的线程都处于活动状态时如果有新的任务进来就会创建一个新的线程去执行任务,同时它还有超时机制,当一个线程闲置超过60秒时就会被回收。
  • ScheduleThreadPool:它是一种拥有固定数量的核心线程和不固定数量的非核心线程的线程池,当非核心线程闲置时会立即被回收。
  • SignleThreadExecutor:它是一种只有一个核心线程的线程池,所有任务都在同一个线程中按顺序执行。

总结

到此这篇关于Android中线程和线程池的文章就介绍到这了,更多相关Android线程和线程池内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android自带的四种线程池使用总结

    在Android开发中,如果我们要执行某个耗时任务,一般都会考虑开启一个线程去处理. 因为我们都知道一个线程run方法执行完毕后,才算真正结束,但是,这只是结束,并没有被回收,会一直闲置在那里,等待GC去回收,所以如果每执行一个任务,我们都new一个线程,那么在某些极端的场景下,是比较消耗内存的. 之前的内存优化的文章中,我讲过关于android中的池的概念,也就是复用的机制,那么对于线程也有个线程池. 这篇文章先简单介绍下Android中自带的四种线程池: 1 .newCachedThread

  • 分析Android中线程和线程池

    目录 前言 HandlerThread IntentService 线程池的好处 ThreadPoolExecutor 线程池的分类 FixedThreadPool CachedThreadPool ScheduledThreadPool SingleThreadExecutor 前言 由于内容过多,所以将分为上下两部分,第一部分主要和大家谈谈Android中的线程,以及在Android中的常用的线程池.第二部分我们一起来了解一下AsyncTask的使用和工作原理. HandlerThread

  • Android开发中线程池源码解析

    线程池(英语:thread pool):一种线程使用模式.线程过多会带来调度开销,进而影响缓存局部性和整体性能.而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务.这避免了在处理短时间任务时创建与销毁线程的代价.线程池不仅能够保证内核的充分利用,还能防止过分调度.可用线程数量应该取决于可用的并发处理器.处理器内核.内存.网络sockets等的数量. 例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销.----摘自维基百科 我们在Android或者Java开发中

  • 浅谈Android中线程池的管理

    说到线程就要说说线程机制 Handler,Looper,MessageQueue 可以说是三座大山了 Handler Handler 其实就是一个处理者,或者说一个发送者,它会把消息发送给消息队列,也就是Looper,然后在一个无限循环队列中进行取出消息的操作 mMyHandler.sendMessage(mMessage); 这句话就是我耗时操作处理完了,我发送过去了! 然后在接受的地方处理!简单理解是不是很简单. 一般我们在项目中异步操作都是怎么做的呢? // 这里开启一个子线程进行耗时操作

  • 浅谈Android 的线程和线程池的使用

    Android 的线程和线程池 从用途上分,线程分为主线程和子线程:主线程主要处理和界面相关的事情,子线程则往往用于耗时操作. 主线程和子线程 主线程是指进程所拥有的线程.Android 中主线程交 UI 线程,主要作用是运行四大组件以及处理它们和用户的交互:子线程的作业则是执行耗时任务. Android 中的线程形态 1.AsyncTask AsyncTask 是一种轻量级的异步任务类,可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新 UI, AsyncTas

  • 一文彻底了解Android中的线程和线程池

    目录 前言 1.主线程和子线程 2.Android中的线程形态 2.1 AsyncTask 2.2 AsyncTask的工作原理 2.3 HandleThread 2.4 IntentService 3.Android中的线程池 3.1 ThreadPoolExecutor 3.2线程池的分类 总结 前言 从用途上来说Android的线程主要分为主线程和子线程两类,主线程主要处理和界面相关的工作,子线程主要处理耗时操作.除Thread之外,Android中还有其他扮演线程的角色如AsyncTas

  • Android 中三种启用线程的方法总结

    在多线程编程这块,我们经常要使用Handler(处理),Thread(线程)和Runnable这三个类,那么他们之间的关系你是否弄清楚了呢? 首先说明Android的CPU分配的最小单元是线程,Handler一般是在某个线程里创建的,因而Handler和Thread就是相互绑定的,一一对应. 而Runnable是一个接口,Thread是Runnable的子类.所以说,他俩都算一个进程. HandlerThread顾名思义就是可以处理消息循环的线程,他是一个拥有Looper的线程,可以处理消息循环

  • 使用 Lambda 取代 Android 中的匿名类

    Lambda是第十一个希腊字母,大写Λ,小写λ,额,跑题了-Lambda表达式 是Java8的新特性之一: Lambda表达式 函数式接口 流API 默认方法 新的Date Time API Lambda表达式 取代了匿名类 ,取消了模板,允许用函数式风格编写代码. 由于最近接触了RxJava,遇到了Lambda,立马就喜欢上了~所以就学习了一下. 本文主要介绍一下Lambda在Android中替代匿名类的部分使用场景. 在Android中使用Lambda gradle-retrolambda

  • Android中检测当前是否为主线程最可靠的解决方法

    如果在Android中判断某个线程是否是主线程?对于这个问题,你可能说根据线程的名字,当然这个可以解决问题,但是这样是最可靠的么?万一某天Google一下子将线程的名字改称其他神马东西呢. 方法揭晓 下面的方法是最可靠的解决方案. 复制代码 代码如下: public static boolean isInMainThread() {       return Looper.myLooper() == Looper.getMainLooper(); } 实际上,写到这里就基本解决了文章标题的问题了

  • android中UI主线程与子线程深入分析

    本文较为深入的分析了android中UI主线程与子线程.分享给大家供大家参考.具体如下: 在一个Android 程序开始运行的时候,会单独启动一个Process.默认的情况下,所有这个程序中的Activity或者Service(Service和 Activity只是Android提供的Components中的两种,除此之外还有Content Provider和Broadcast Receiver)都会跑在这个Process. 一个Android 程序默认情况下也只有一个Process,但一个Pr

  • android开发教程之子线程中更新界面

    每个Handler对象与创建它的线程相关联,并且每个Handler对象只能与一个线程相关联.Handler一般有两种用途:1)执行计划任务,你可以再预定的实现执行某些任务,可以模拟定时器.2)线程间通信.在Android的应用启动时,会创建一个主线程,主线程会创建一个消息队列来处理各种消息.当你创建子线程时,你可以再你的子线程中拿到父线程中创建的Handler对象,就可以通过该对象向父线程的消息队列发送消息了.由于Android要求在UI线程中更新界面,因此,可以通过该方法在其它线程中更新界面.

  • Android使用Sensor感应器实现线程中刷新UI创建android测力计的功能

    本文实例讲述了Android使用Sensor感应器实现线程中刷新UI创建android测力计的功能.分享给大家供大家参考,具体如下: 前面一篇<Android基于Sensor感应器获取重力感应加速度的方法>我们介绍了sensor的基本知识以及一个使用其中加速度感应器获取数据的例子. 前面提到过一个问题,就是说感应器刷新频率太快,假如我们要做一个UI中,需要根据方向数据绘制一个一个移动的箭头,那么就要太过频繁的刷新绘制界面,占用很多的资源,体验性也会很差,<android 2高级编程>

  • Android 中通过实现线程更新Progressdialog (对话进度条)

    作为开发者我们需要经常站在用户角度考虑问题,比如在应用商城下载软件时,当用户点击下载按钮,则会有下载进度提示页面出现,现在我们通过线程休眠的方式模拟下载进度更新的演示,如图(这里为了截图方便设置对话进度条位于屏幕上方): layout界面代码(仅部署一个按钮): <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.androi

  • Android中Java根据文件头获取文件类型的方法

    本文实例讲述了Android中Java根据文件头获取文件类型的方法.分享给大家供大家参考,具体如下: 前面讲过Android系统内部的MediaFile类来获取文件类型的办法,这个类主要是根据文件的扩展名来判断,其准确性不是很好.具体可查看Android系统使用MediaFile类判断音频文件类型.其实,获取文件类型最好的办法便是根据文件头信息来判断.下面贴出相关代码: public class FileType { public static final HashMap<String, Str

随机推荐