Android:Android线程和线程池的作用

article/2025/9/28 23:24:22

文章目录

  • 前言
  • 一、线程池概念
    • 好处
    • 线程池的创建
    • 参数含义
    • 工作机制
    • RejectedExecutionHandler(饱和策略)
  • 二、Android线程形态
    • 1.AsyncTask
      • 特点
      • 作用
      • 缺点
      • 生命周期
        • 注意事项
      • 具体使用
      • 源码分析
      • 小结
    • 2.HandlerThread
      • 特点
      • 作用
      • 具体使用
    • 3.IntentService
      • 源码分析
  • 总结


前言

这篇笔记是笔者关于Android线程和线程池作用的学习记录

一、线程池概念

线程池: 简单理解,它就是一个管理线程的池子。

  • 它帮我们管理线程,避免增加创建线程和销毁线程的资源损耗。因为线程其实也是一个对象,创建一个对象,需要经过类加载过程,销毁一个对象,需要走GC垃圾回收流程,都是需要资源开销的。
  • 提高响应速度。 如果任务到达了,相对于从线程池拿线程,重新去创建一条线程执行,速度肯定慢很多。
  • 重复利用。 线程用完,再放回池子,可以达到重复利用的效果,节省资源。

好处

  • 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗
  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

线程池的创建

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {
}

参数含义

  • corePoolSize:线程池中的核心线程数
  • maximumPoolSize:线程池中允许的最大线程数
  • keepAliveTime:线程空闲时的存活时间(只在线程数大于corePoolSize时才有用)
  • unit:keepAliveTime的时间单位
  • workQueue:阻塞队列.当线程池中的线程数超过它的corePoolSize的时候,线程会进入阻塞队列进行阻塞等待
  • threadFactory:创建线程的工厂(线程的命名规则是“pool-数字-thread-数字”)
  • handler:线程池的饱和策略.

工作机制

  • 当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;
  • 如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;
  • 如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize.
  • 当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务

RejectedExecutionHandler(饱和策略)

二、Android线程形态

除了传统的Thread以外,Android还有AsyncTask、HandlerThread和IntentService等形态

1.AsyncTask

轻量级的异步任务,主要在线程池中执行任务,并将执行的进度和最终结果传递给主线程用于更新UI
从实现上来说,AsyncTask就是对Thread和Handler进行了一层封装,但是AsyncTask并不适合特别耗时的任务,特别耗时的任务,建议使用线程池

特点

  • 轻量型异步类
  • 使用时需要实现其子类
    在这里插入图片描述

作用

  • 实现多线程
  • 异步通信 消息传递
  • 在Android 11中被标记为过时,官方推荐使用Kotlin的协程解决相关问题

缺点

  • 容易导致内存泄漏
  • 忘记回调
  • 横竖屏切换导致崩溃
  • 不同版本的AsyncTask的兼容问题

生命周期

在这里插入图片描述

注意事项

1.生命周期

  • AsyncTask不与任何组件绑定生命周期
    解决方案:
  • 在Activity 或 Fragment中使用 AsyncTask时,最好在Activity 或 Fragment的onDestory()调用 cancel(boolean);//

2.内存泄漏

  • 若AsyncTask被声明为Activity的非静态内部类,当Activity需销毁时,会因AsyncTask保留对Activity的引用 而导致Activity无法被回收,最终引起内存泄露
    解决方案:
  • AsyncTask应被声明为Activity的静态内部类

3.屏幕旋转
当Activity重新创建时(屏幕旋转 / Activity被意外销毁时后恢复),之前运行的AsyncTask(非静态的内部类)持有的之前Activity引用已无效,故复写的onPostExecute()将不生效,即无法更新UI操作
使用建议
在Activity恢复时的对应方法 重启 任务线程

具体使用

具体使用过程如下

  • 创建 AsyncTask 子类 & 根据需求实现核心方法
  • 创建 AsyncTask子类的实例对象(即 任务实例)
  • 手动调用execute(()从而执行异步线程任务
class MyAsyncTask extends AsyncTask<String,Integer,String>{@Overrideprotected void onPreExecute() {//数据初始化操作super.onPreExecute();}@Overrideprotected String doInBackground(String... strings) {//耗时操作,并将结果返回return null;}@Overrideprotected void onProgressUpdate(Integer... values) {//对进度条进行更新操作super.onProgressUpdate(values);}@Overrideprotected void onPostExecute(String s) {//UI更新操作super.onPostExecute(s);}}
  • onPreExecute():我们在进行初始化数据的时候调用这个方法,这个方法是在主线程执行。
  • doInBackground():我们在进行耗时操作的时候调用这个方法,所有的耗时操作都在这个里面进行操作。并且将耗时操作的结果返回回去。
  • onProgressUpdata():对控件的进度进行操作。
  • onPostExecute():这个方法里面会拿到doInBackground()方法中返回的参数结果,我们在这个方法里面可以对UI进行操作,从而达到更新UI的结果。

AsyncTask的Demo

源码分析

 	public AsyncTask(@Nullable Looper callbackLooper) {mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()? getMainHandler(): new Handler(callbackLooper);//要执行的任务,是对Callable接口的封装。可以获取到返回值mWorker = new WorkerRunnable<Params, Result>() {public Result call() throws Exception {mTaskInvoked.set(true);Result result = null;try {Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);result = doInBackground(mParams);Binder.flushPendingCommands();} catch (Throwable tr) {mCancelled.set(true);throw tr;} finally {postResult(result);}return result;}};//AsyncTask要执行的任务分返回结果mFuture = new FutureTask<Result>(mWorker) {@Overrideprotected void done() {try {postResultIfNotInvoked(get());} catch (InterruptedException e) {android.util.Log.w(LOG_TAG, e);} catch (ExecutionException e) {throw new RuntimeException("An error occurred while executing doInBackground()",e.getCause());} catch (CancellationException e) {postResultIfNotInvoked(null);}}};}     

执行流程如下:

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {if (mStatus != Status.PENDING) {switch (mStatus) {case RUNNING:throw new IllegalStateException("Cannot execute task:"+ " the task is already running.");case FINISHED:throw new IllegalStateException("Cannot execute task:"+ " the task has already been executed "+ "(a task can be executed only once)");}}mStatus = Status.RUNNING;onPreExecute();mWorker.mParams = params;exec.execute(mFuture);return this;
}

先回在主线程调用onPreExecute()方法。 将mFuture传递给了AsyncTask的执行器进行执行。AsyncTask的执行器缺省是sDefaultExecutor。

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();@UnsupportedAppUsage
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;private static class SerialExecutor implements Executor {//双端队列final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();Runnable mActive;public synchronized void execute(final Runnable r) {mTasks.offer(new Runnable() {public void run() {try {r.run();} finally {scheduleNext();}}});if (mActive == null) {scheduleNext();}}protected synchronized void scheduleNext() {if ((mActive = mTasks.poll()) != null) {THREAD_POOL_EXECUTOR.execute(mActive);}}
}

每次调用execute,就创建一个Runnable匿名内部类对象,这个对象存入mTasks,在匿名内部类的run函数里面调用传入参数r.run()。然后通过一个scheduleNext函数把mTasks里面的所有对象通过THREAD_POOL_EXECUTOR.execute(mActive)执行一遍。

SerialExecutor类会把所有的任务丢入一个容器,之后把容器里面的所有对象一个一个的排队(串行化)执行THREAD_POOL_EXECUTOR.execute(mActive);

处理任务的线程池

static {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(), sThreadFactory);
}

SynchronousQueue没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。
以前用的是LinkedBlockingQueue(最大容量是128).

结果和进度的通知

private Result postResult(Result result) {@SuppressWarnings("unchecked")Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this, result));message.sendToTarget();return result;
}

进入消息处理:

private static class InternalHandler extends Handler {public InternalHandler(Looper looper) {super(looper);}@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})@Overridepublic void handleMessage(Message msg) {AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;switch (msg.what) {case MESSAGE_POST_RESULT://结果// There is only one resultresult.mTask.finish(result.mData[0]);break;case MESSAGE_POST_PROGRESS://进度result.mTask.onProgressUpdate(result.mData);break;}}
}

小结

在创建了AsyncTask的时候,会默认创建两个线程池SerialExecutor和ThreadPoolExecutor,SerialExecutor负责将任务串行化,ThreadPoolExecutor是真正执行任务的地方,且无论有多少个AsyncTask实例,两个线程池都会只有一份。

在execute中,会执行run方法,当执行完run方法后,会调用scheduleNext()不断的从双端队列中轮询,获取下一个任务并继续放到一个子线程中执行,直到异步任务执行完毕。

在执行完onPreExecute()方法之后,执行了doInBackground()方法,然后就不断的发送请求获取数据;在这个AsyncTask中维护了一个InternalHandler的类,这个类是继承Handler的,获取的数据是通过handler进行处理和发送的。在其handleMessage方法中,将消息传递给onProgressUpdate()进行进度的更新,也就可以将结果发送到主线程中,进行界面的更新了。

通过观察代码我们可以发现,每一个new出的AsyncTask只能执行一次execute()方法,多次运行将会报错,如需多次,需要新new一个AsyncTask(参见如下代码)

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {if (mStatus != Status.PENDING) {switch (mStatus) {case RUNNING:throw new IllegalStateException("Cannot execute task:"+ " the task is already running.");case FINISHED:throw new IllegalStateException("Cannot execute task:"+ " the task has already been executed "+ "(a task can be executed only once)");}}mStatus = Status.RUNNING;onPreExecute();mWorker.mParams = params;exec.execute(mFuture);return this;
}

2.HandlerThread

HanderThread其实就是一个内部包含了Looper的线程,说白了就是多线程+Handler的简化版
我们看看其Run方法便知道了

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

特点

  • HandlerThread本质上是一个Thread对象,只不过其内部帮我们创建了该线程的Looper和MessageQueue;

作用

  • 通过HandlerThread我们不但可以实现UI线程与子线程的通信同样也可以实现子线程与子线程之间的通信;

  • HandlerThread在不需要使用的时候需要手动的回收掉;

具体使用

/*** 测试HandlerThread的基本使用*/HandlerThread mHandlerThread = new HandlerThread("myHandlerThreand");mHandlerThread.start();// 创建的Handler将会在mHandlerThread线程中执行final Handler mHandler = new Handler(mHandlerThread.getLooper()) {@Overridepublic void handleMessage(Message msg) {Log.i("tag", "接收到消息:" + msg.obj.toString());}};title = (TextView) findViewById(R.id.title);title.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Message msg = new Message();msg.obj = "11111";mHandler.sendMessage(msg);msg = new Message();msg.obj = "2222";mHandler.sendMessage(msg);}});public HandlerThread(String name) {super(name);mPriority = Process.THREAD_PRIORITY_DEFAULT;}protected void onDestroy() {super.onDestroy();mHandlerThread.quit();}

3.IntentService

IntentService 是Service 的子类,它使用工作线程逐一处理所有启动请求,如果您不要求服务同时处理多个请求,这是最好的选择。 您只需实现 onHandIntent方法即可,该方法会接收每个启动请求的 Intent,使您能够执行后台工作。

IntentService可以执行后台耗时的任务,并且因为它是服务,优先级比单纯的线程要高很多,所以IntentService比较适合一些高优先级的后台服务

IntentService扩展类样例

public class MyIntentService extends IntentService {public static final String TAG ="MyIntentService";/*** Creates an IntentService.  Invoked by your subclass's constructor.** @param name Used to name the worker thread, important only for debugging.*/public MyIntentService() {super("MyIntentService");}@Overrideprotected void onHandleIntent(@Nullable Intent intent) {// 这里已经是工作线程,在这里执行操作就行boolean isMainThread =  Thread.currentThread() == Looper.getMainLooper().getThread();Log.i(TAG,"is main thread:"+isMainThread);// 执行耗时下载操作mockDownload();}/*** 模拟执行下载*/private void mockDownload(){try {Thread.sleep(5000);Log.i(TAG,"下载完成...");}catch (Exception e){e.printStackTrace();}}}

源码分析

IntentService自动为我们开启一个线程来进行耗时操作,并且在操作完成后自动停止服务,其源码如下

   	// 1,有一个Looper 变量和一个ServiceHandler 变量,ServiceHander 继承Handler 处理消息private volatile Looper mServiceLooper;private volatile ServiceHandler mServiceHandler;private String mName;private boolean mRedelivery;private final class ServiceHandler extends Handler {public ServiceHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {// 在工作线程中调用onHandleIntent,子类根据Intent传递的数据执行具体的操作onHandleIntent((Intent)msg.obj);// 任务执行完毕后,自动停止ServicestopSelf(msg.arg1);}}//2, 在OnCreate 方法中,创建了一个线程HandlerThread ,并启动线程// 然后获取工作线程的Looper ,并用Looper 初始化Handler(我们都知道Handler 的创建需要一依赖Looper)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);}//3, 在onStart()方法中发送消息给Handler,并且把Intent 传给了Handler 处理@Overridepublic void onStart(@Nullable Intent intent, int startId) {Message msg = mServiceHandler.obtainMessage();msg.arg1 = startId;msg.obj = intent;mServiceHandler.sendMessage(msg);}// 4,onStartCommand 直接调用的是onStart方法public int onStartCommand(@Nullable Intent intent, int flags, int startId) {onStart(intent, startId);return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;}// 5 最后就是一个子类需要实现的抽象方法,这个方法在handleMessage中调用,也就是在工作线程中执行。protected abstract void onHandleIntent(@Nullable Intent intent);

需要注意的是,在IntentService第一次被启动时,它的OnCreate()方法会被调用,而后启动IntentService(),会调用它的onStartCommand()方法

总结

IntentService是Service 的子类,默认给我们开启了一个工作线程执行耗时任务,并且执行完任务后自 动停止服务。扩展IntentService比较简单,提供一个构造方法和实现onHandleIntent 方法就可了,不用重写父类的其他方法。但是如果要绑定服务的话,还是要重写onBind 返回一个IBinder 的。使用Service 可以同时执行多个请求,而使用IntentService 只能同时执行一个请求。

参考文章
Android Service和IntentService知识点详细总结
线程池和AsyncTask的源码分析


http://chatgpt.dhexx.cn/article/c5UQtZT2.shtml

相关文章

线程池作用和参数原理

线程池的作用 减少资源的开销 减少了每次创建线程、销毁线程的开销。 提高响应速度每次请求到来时&#xff0c;由于线程的创建已经完成&#xff0c;故可以直接执行任务&#xff0c;因此提高了响应速度。提高线程的可管理性 线程是一种稀缺资源&#xff0c;若不加以限制&#x…

线程池作用、用法以及原理

线程池 作用用法建议设定大小快捷构造线程池submit与executeshutdown与shutdownNowFuture与FutureTast代码 状态底层原理继承关系主要参数工作原理饱和策略连接复用 作用 1&#xff0c;降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗 2&#xff0c;提高…

线程池-线程池的好处

1.线程池的好处。 线程使应用能够更加充分合理的协调利用cpu 、内存、网络、i/o等系统资源。 线程的创建需要开辟虚拟机栈&#xff0c;本地方法栈、程序计数器等线程私有的内存空间。 在线程的销毁时需要回收这些系统资源。频繁的创建和销毁线程会浪费大量的系统资源&#xff0…

什么是线程池,线程池的作用

线程池&#xff0c;--其实就是一个 容纳多个线程的容器 &#xff0c;其中的线程可以反复使用&#xff0c;省去了频繁创建线程对象的操作 &#xff0c;--无需反复创建线程而消耗过多资源。 创建销毁线程是一个非常消耗性能的。 我们详细的解释一下为什么要使用线程池&#xff1f…

华为防火墙实战配置教程,太全了

防火墙是位于内部网和外部网之间的屏障&#xff0c;它按照系统管理员预先定义好的规则来控制数据包的进出。防火墙是系统的第一道防线&#xff0c;其作用是防止非法用户的进入。 本期我们一起来总结下防火墙的配置&#xff0c;非常全面&#xff0c;以华为为例。 防火墙的配置…

防火墙配置(命令)

拓扑图&#xff1a; 目的&#xff1a;PC1和PC2相互ping通。 配置命令&#xff1a; FW1: //添加端口IP [SRG-GigabitEthernet0/0/0]interface GigabitEthernet0/0/1 [SRG-GigabitEthernet0/0/1]ip add 192.168.1.1 24 [SRG]inter g0/0/2 [SRG-GigabitEthernet0/0/2]ip add 10.…

防火墙基础配置(二)

拓补图&#xff1a; 方案一&#xff08;子接口的形式&#xff09; 实验目的&#xff1a;解决防火墙上的接口不足以为其他区域服务的问题&#xff0c;比方说防火墙上只有两个接口&#xff0c;但是有三个区域&#xff0c;那这个实验的目的为了解决防火墙上的接口不足以为多区域提…

H3C防火墙基础配置2-配置安全策略

1 安全策略简介 安全策略对报文的控制是通过安全策略规则实现的&#xff0c;规则中可以设置匹配报文的过滤条件&#xff0c;处理报文的动作和对于报文内容进行深度检测等功能。 &#xff08;1&#xff09;规则的名称和编号 安全策略中的每条规则都由唯一的名称和编号标识。名称…

H3C防火墙-安全域配置举例

1. 组网需求 某公司以 Device 作为网络边界安全防护设备&#xff0c;连接公司内部网络和 Internet。公司只对内部提供Web 服务&#xff0c;不对外提供这些服务。现需要在设备上部署安全域&#xff0c;并基于以下安全需求进行域间策略 的配置。 • 与接口 GigabitEthernet1/0/1 …

Firewalld防火墙实例配置

文章目录 环境拓扑需求描述一、环境配置二、防火墙配置1、在网站服务器上配置防火墙2、网关服务器配置防火墙3、企业内网访问外网web服务器4、外网web服务器访问企业内部网站服务器 三、总结问题总结解决方案 环境拓扑 需求描述 1、 网关服务器连接互联网网卡ens33地址为100.1…

【Linux】配置网络和firewall防火墙(超详细介绍+实战)

&#x1f947;&#x1f947;【Liunx学习记录篇】&#x1f947;&#x1f947; 篇一&#xff1a;【Linux】VMware安装unbuntu18.04虚拟机-超详细步骤(附镜像文件&#xff09; 篇二&#xff1a;【Linux】ubuntu18.04系统基础配置及操作 篇三&#xff1a;【Linux】用户与组的操作详…

防火墙配置

防火墙&#xff08;Firewall&#xff09;&#xff0c;也称防护墙。它是一种位于内部网络与外部网络之间的网络安全系统。一项信息安全的防护系统&#xff0c;依照特定的规则&#xff0c;允许或是限制传输的数据通过。防火墙对于我们的网络安全的重要性不言而喻 但是在实际的开发…

H3C防火墙-安全策略典型配置举例

基于 IP 地址的安全策略配置举例 1.组网需求 • 某公司内的各部门之间通过 Device 实现互连&#xff0c;该公司的工作时间为每周工作日的 8 点到 18点。 • 通过配置安全策略规则&#xff0c;允许总裁办在任意时间、财务部在工作时间通过 HTTP 协议访问财务数据库服务器的 Web…

华为防火墙配置教程

01 了解防火墙基本机制 配置防火墙之前请了解防火墙基本工作机制。 1.1 什么是防火墙 防火墙是一种网络安全设备&#xff0c;通常位于网络边界&#xff0c;用于隔离不同安全级别的网络&#xff0c;保护一个网络免受来自另一个网络的攻击和入侵。这种“隔离”不是一刀切&#x…

防火墙基本配置

防火墙 种类 1.包过滤技术 「 静态防火墙 动态防火墙 」 netfilter 真正的配置 位于linux内核的包过滤功能体系 称为linux防火墙的“内核态” iptables 防火墙的配置 工具 主要针对 网络层 针对IP数据包 「体现在对包的IP地址、端口等信息处理」 链表结构 链 ---- 容纳 规则…

防火墙详解(三)华为防火墙基础安全策略配置(命令行配置)

实验要求 根据实验要求配置防火墙&#xff1a; 合理部署防火墙安全策略以及安全区域实现内网用户可以访问外网用户&#xff0c;反之不能访问内网用户和外网用户均可以访问公司服务器 实验配置 步骤一&#xff1a;配置各个终端、防火墙端口IP地址 终端以服务器为例&#x…

防火墙基础配置

状态防火墙 状态检测防火墙&#xff08;Stateful Firewall&#xff09;是一种网络安全设备&#xff0c;它可以检测和过滤网络流量&#xff0c;以保护网络不受未经授权的访问和攻击。 与传统的包过滤防火墙不同&#xff0c;状态检测防火墙可以跟踪网络连接的状态&#xff0c;并…

10分钟教你完全掌握防火墙配置!!!!!

今日提问 1.防火墙支持那些NAT技术&#xff0c;主要应用场景是什么&#xff1f; 2.当内网PC通过公网域名解析访问内网服务器时&#xff0c;会存在什么问题&#xff0c;如何解决&#xff1f;请详细说明 3.防火墙使用VRRP实现双机热备时会遇到什么问题&#xff0c;如何解决&…

H3C简单的防火墙配置

这里写目录标题 实验拓扑实验需求配置过程1.配置ip地址&#xff08;略&#xff09;2.配置去往公网的默认路由3.将端口绑定在信任域和不信任域4.配置ipv4安全模板5.配置ospf将内网的连通性完成6.配置nat &#xff08;easy ip的方式&#xff09;使内网PC可以访问外网 测试 实验拓…

防火墙的基础配置(一)

拓补图&#xff1a; 注意事项&#xff1a; 不要连接着防火墙的g0/0/0口&#xff0c;这个口是防火墙的管理端口一定要将接口划分区域&#xff0c;防火墙有四个区域&#xff0c;分别是local、trust、untrust、dmz&#xff0c;优先级分别是100、85、50、5&#xff0c;优先级越高说…