Android开发ThreadPoolExecutor与自定义线程池详解

作者:Coolbreeze 时间:2022-03-17 18:16:22 

概述

1、ThreadPoolExecutor作为java.util.concurrent包对外提供基础实现,以内部线程池的形式对外提供管理任务执行,线程调度,线程池管理等等服务;

2、Executors方法提供的线程服务,都是通过参数设置来实现不同的线程池机制。

3、先来了解其线程池管理的机制,有助于正确使用,避免错误使用导致严重故障。同时可以根据自己的需求实现自己的线程池

ThreadPoolExecutor 类

构造方法

public ThreadPoolExecutor(
int corePoolSize,//核心线程数量:如果当前运行的线程数量没有达到 corePoolSize,则新建一个线程,否则加入到任务队列中
int maximumPoolSize,//最大线程数:当前系统最多存在的线程数
long keepAliveTime,//最大空闲时间:线程空闲的最大时间,超出时间该线程会销毁;设置allowCodeThreadTimeOut(true/false),可控制核心线程是否销毁,默认false 表示允许核心线程即使超过最大线程时间也不会销毁
TimeUnit unit,//时间单位:线程空闲的最大时间的单位
BlockingQueue<Runnable> workQueue,//任务队列:核心线程数量满了之后,提交的任务加入到队列中,等待核心线程数减少后再去创建线程;当任务队列已满,但没有达到最大线程数时,则新建非核心线程
ThreadFactory threadFactory,//线程工厂:自定义线程的创建
RejectedExecutionHandler handler//饱和处理机制:当任务队列已满且达到最大线程数时,采取的措施
)

线程池原理

线程池底层使用**堵塞式队列 BlockingQueue **。

队列遵从:先进先出,后进后出原则。 阻塞队列(BlockingQueue)和非阻塞队列(ConcurrentLinkedQueue )区别:

* 和有界队列:

ConcurrentLinkedQueue 是 * 队列,不用设置长度,可以随便存放值(其实是jdk伪造的,最大长度是Integer的最大值) BlockingQueue 是有界队列,需要设置长度。 注意:如果BlockingQueue 不设置等待时间就是非阻塞队列

存入:

非阻塞队列:如果存放超出了队列总数,添加不进去,就会丢失。 阻塞队列:如果存放超出了队列总数,进行等待,直到有队列出列,或者超时设置的等待时间)

获取:

非阻塞队列:如果为空时,返回空。 阻塞队列:如果为空时,进行等待,直到有新的队列入列,或者超过设置的等待时间

创建线程池的构造方法

ThreadPoolExecutor(int corePoolSize,
                  int maximumPoolSize,
                  long keepAliveTime,
                  TimeUnit unit,
                  BlockingQueue<Runnable> workQueue)
  • ThreadPoolExecutor

  • 参数说明

  • 核心线程大小(corePoolSize)

  • 最大线程大小(maximumPoolSize)

  • 终止时间(keepAliveTime)

  • Unit 超时时间

  • workQueue 线程容器

自定义线程池

1、编写任务类

public class MyTask implements Runnable{
//任务id
private int id;
public MyTask(int id){
   this.id=id;
}
@Override
public void run() {
   String name=Thread.currentThread().getName();
   System.out.println("线程:"+name+"-->即将执行任务"+id);
   try {
       Thread.sleep(200);
   } catch (InterruptedException e) {
       e.printStackTrace();
   }
   System.out.println("线程:"+name+"执行完成"+id);
}
@Override
public String toString() {
   return "MyTask{" +
           "id=" + id +
           '}';
}
}

2、编写线程类,用于执行任务

public class MyThread extends  Thread{
   private List<Runnable> tasks;
   public MyThread(String name, List<Runnable> tasks){
       super(name);
       this.tasks=tasks;
   }
   @Override
   public void run() {
       while (tasks.size() > 0){
           Runnable r= tasks.remove(0);
           r.run();
       }
   }
}

3、编写线程池类,用于管理线程的执行

public class MyThreadPool {
   private List<Runnable>  tasks = Collections.synchronizedList(new LinkedList<>());
   /**
    * 当前线程数
    */
   private int num;
   /**
    * 核心线程数
    */
   private int corePoolSize;
   /**
    * 最大线程数
    */
   private int maxSize;
   /**
    * 任务队列数
    */
   private int workSize;
   public MyThreadPool(int corePoolSize, int maxSize, int workSize) {
       this.corePoolSize = corePoolSize;
       this.maxSize = maxSize;
       this.workSize = workSize;
   }
   /**
    * 提交任务
    */
   public void submit(Runnable r){
       if (tasks.size()>=workSize && tasks.size() > maxSize){
           System.out.println("任务:"+r+"被丢弃了");
       }else{
           tasks.add(r);
           execTask(r);
       }
   }
   public void execTask(Runnable r){
       if (corePoolSize > num){
           new MyThread("核心线程:"+num,tasks).start();
           num++;
       }else  if(num < maxSize){
           new MyThread("非核心线程:"+num,tasks).start();
           num++;
       }else{
           System.out.println("任务:"+r+"被缓存了");
       }
   }
}

4、测试

public class Demo {
   public static void main(String[] args) {
       MyThreadPool myThreadPool = new MyThreadPool(2, 4, 20);
       for (int i =0;i< 300;i++){
           MyTask myTask = new MyTask(i);
           myThreadPool.submit(myTask);
       }
   }
}

文末

1、用ThreadPoolExecutor自定义线程池,看线程是的用途,如果任务量不大,可以用 * 队列,如果任务量非常大,要用有界队列,防止OOM

2、如果任务量很大,还要求每个任务都处理成功,要对提交的任务进行阻塞提交,重写拒绝机制,改为阻塞提交。保证不抛弃一个任务

3、最大线程数一般设为2N+1最好,N是CPU核数

4、核心线程数,看应用,如果是任务,一天跑一次,设置为0,合适,因为跑完就停掉了,如果是常用线程池,看任务量,是保留一个核心还是几个核心线程数

5、如果要获取任务执行结果,用CompletionService,但是注意,获取任务的结果的要重新开一个线程获取,如果在主线程获取,就要等任务都提交后才获取,就会阻塞大量任务结果,队列过大OOM,所以最好异步开个线程获取结果

来源:https://juejin.cn/post/7168453723591540743

标签:Android,ThreadPoolExecutor,线程池
0
投稿

猜你喜欢

  • C#获取Word文档中所有表格的实现代码分享

    2023-05-12 10:01:46
  • Java求1+2!+3!+...+20!的和的代码

    2021-12-16 10:55:23
  • C# Winform 调用系统接口操作 INI 配置文件的代码

    2023-03-04 11:49:54
  • Spring整合WebSocket应用示例(上)

    2023-05-05 10:09:21
  • GC调优实战之高分配速率High Allocation Rate

    2021-09-30 22:03:21
  • 修改maven本地仓库路径的方法

    2022-08-09 13:44:16
  • MybatisPlus代码生成器含XML文件详解

    2023-10-25 21:51:26
  • Android GPS定位测试(附效果图和示例)

    2023-05-11 17:38:10
  • SSM框架+Plupload实现分块上传大文件示例

    2023-06-01 01:58:21
  • JVM分配和回收堆外内存的方式与注意点

    2021-07-25 08:53:34
  • Java毕业设计实战之药店信息管理系统的实现

    2022-03-07 11:55:37
  • Android仿QQ在状态栏显示登录状态效果

    2021-10-09 04:12:06
  • C#中CheckedListBox控件的用法实例

    2021-08-05 06:03:29
  • Android的异步任务AsyncTask详解

    2021-10-13 02:58:28
  • C# this关键字的四种用法

    2022-10-06 07:09:22
  • 简单了解Spring中BeanFactory与FactoryBean的区别

    2022-01-14 03:59:20
  • C#实现两个时间相减的方法

    2022-12-21 03:08:29
  • C# 文件上传下载(Excel导入,多线程下载)功能的实现代码

    2021-12-09 20:16:34
  • SpringMVC bean加载控制的实现分析

    2023-08-08 18:46:19
  • Java动态代理分析及理解

    2021-10-21 14:59:58
  • asp之家 软件编程 m.aspxhome.com