线程池ThreadPoolExecutor简介

线程池ThreadPoolExecutor简介

线程池的实现方式

java中实现线程池的类主要是ThreadPoolExecutor这个类

主要的构造方法有

image-20191119172740407

来看一下最全的构造方法

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

来介绍一下它的参数

  1. int corePoolSize

    核心线程数,默认情况下核心线程会一直存活,即使处于闲置状态.但是如果将allowCoreThreadTimeOut设置为true的话,那么闲置的核心线程也会和非核心线程一样有超时策略.这个超时值由keepAliveTime指定.

  2. int maximumPoolSize

    最大线程数

  3. long keepAliveTime

    非核心线程数的存活时间.如果非核心线程处于闲置状态,超过这个时长就会被回收.

  4. TimeUnit unit

    存活时间的时间单位

  5. BlockingQueue<Runnable> workQueue

    任务队列,当线程池创建的线程数量大于 corePoolSize 后,新来的任务将会加入到堵塞队列(workQueue)中,等待有空闲线程来执行.常用的有:

    1. LinkedBlockingQueue 默认长度是 Integer.MAX_VALUE也可以通过构造方法设置一个长度.
    2. SynchronousQueue 以一个很特殊的队列. 它不会保存任务,每一个新增任务的线程必须等待另一个线程取出任务,也可以把它看成容量为0的队列.简单来说就是有了任务之后直接提交给thread运行.
  6. ThreadFactory threadFactory

    ThreadFactory是一个接口,用于创建新的线程

    public interface ThreadFactory {
        Thread newThread(Runnable r);
    }
    
  7. RejectedExecutionHandler handler

    RejectedExecutionHandler也是一个接口.当线程池已满或者线程队列已满时,会调用rejectedExecution实行拒绝策略

    public interface RejectedExecutionHandler {
        void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
    }
    

    有为四种策略

    1. AbortPolicy 直接抛出 RejectedExecutionException 异常。
    2. CallerRunsPolicy 会在运行线程池的线程中处理被拒绝的任务。
    3. DiscardOldestPolicy 放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。
    4. DiscardPolicy 线程池将丢弃被拒绝的任务。

    默认是AbortPolicy,直接抛出 RejectedExecutionException 异常。

线程池的execute规则

先来看看源码

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    else if (!addWorker(command, false))
        reject(command);
}
  1. 当线程数**小于corePoolSize**时,会直接启动一个线程来执行任务.这个线程也叫核心线程.
  2. 当线程数**大于等于corePoolSize**时,会将任务插入到任务队列中.
  3. 当任务队列为SynchronousQueue,因为他的容量为(其实是不保存任务),会直接启动一个非核心线程来执行.
  4. 或者当任务队列已满时,也会直接启动一个非核心线程来执行
  5. 如果线程数大于maximumPoolSize,就会执行拒绝策略.调用handler.rejectedExecution

怎么使用线程池

先说说禁止怎么使用吧

根据***阿里巴巴Android开发手册***

  • 【强制】新建线程时,必须通过线程池提供(AsyncTask 或者 ThreadPoolExecutor 或者其他形式自定义的线程池),不允许在应用中自行显式创建线程。

    说明:

    使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。另外创建匿名线程不便于后续的资源使用分析, 对性能分析等会造成困扰。

  • 【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方 式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

    说明:

    Executors 返回的线程池对象的弊端如下:

    1. FixedThreadPool 和 SingleThreadPool : 允 许 的 请 求 队 列 长 度 为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM;
    2. CachedThreadPool 和 ScheduledThreadPool : 允 许 的 创 建 线 程 数 量 为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

正例:

int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
int KEEP_ALIVE_TIME = 1;
TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>(); 
ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES, NUMBER_OF_CORES*2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT,taskQueue, new BackgroundThreadFactory(), new DefaultRejectedExecutionHandler());

我觉得LinkedBlockingQueue应该设置一个长度比较好.

可以模仿AsyncTask中对线程池的使用.

int CPU_COUNT = Runtime.getRuntime().availableProcessors();
int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
int KEEP_ALIVE_SECONDS = 30;

final ThreadFactory threadFactory = new ThreadFactory() {
  private final AtomicInteger mCount = new AtomicInteger(1);

  public Thread newThread(Runnable r) {
    return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
  }
};

final BlockingQueue<Runnable> poolWorkQueue =
  new LinkedBlockingQueue<>(128);

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
  CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
  poolWorkQueue, threadFactory);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值