线程池知识点总结

240 阅读4分钟

概念

Android 中线程池的真正实现为ThreadPoolExecutor,使用线程池有以下优点

  • 线程可以复用,避免重复创建线程和销毁带来的性能消耗
  • 能够对创建的线程进行简单的管理
  • 可以控制线程池中的最大并发数

执行流程

核心参数

  • corePoolSize

    线程池中的核心线程数,默认情况下核心线程即使在空闲状态也会存活

  • maximumPoolSize

    线程池中的线程数量最大数

  • keepAliveTime

    非核心线程空闲存活时间大小,超过这个时间线程就会被回收

  • unit

    空闲存活时间单位

  • workQueue

    存放任务的阻塞队列

  • threadFactory

    创建线程的工厂

  • ExecutionHandler

    线程池的饱和策略,当线程池无法执行新的任务的时候的处理策略

线程池状态

  • RUNNING

能接受新的任务,并且也能处理阻塞队列里的任务

  • SHUTDOWN

不再接受新的任务,会继续处理阻塞队列中的任务

  • STOP

不接受新任务也不出里阻塞队列中的任务,而且会中断正在执行任务的线程

  • TIDYING

所有任务已完成,有效线程数为0

  • TERMINATED

执行teminated 方法之后进入该状态

任务阻塞队列

  • ArrayBlockingQueue

    基于数组实现的有界阻塞队列,一把锁,两个Condition,

  • LinkBlockingQueue

    基于链表的阻塞队列,头尾一把锁,两个Condition,newFixedThreadPool和newSingleThreadExecutor里面用的就是这种

  • DelayQueue

    DelayQueue 是一个支持延时获取元素的阻塞队列, 内部采用优先队列 PriorityQueue 存储元素,同时 元素必须实现 Delayed 接口;在创建元素时可以指定多久才可以从队列中获取当前元素,只有在延迟期满时才能从队列中提取元素

  • PriorityBlockingQueue

    优先级队列是一个基于堆的无界并发安全的优先级队列

  • SynchronousQueue

    synchronousQueue是一个没有数据缓冲的阻塞队列,生产者线程对其的插入操作put()必须等待消费者的移除操作take(),反过来也一样。在cachedThreadPool的任务队列中用到该队列

拒绝策略

四种拒绝策略都是实现这个接口RejectedExecutionHandler 如果添加进来一个任务,线程池的任务数大于最大任务队列和最大线程数的总和,则会调用设置的handler来处理添加进来的任务。

  • AbortPolicy

直接拒绝执行且抛出异常

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
  • DiscardPolicy

    只拒绝执行

  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
  • DiscardOldestPolicy

    丢弃最旧的任务然后执行新的任务

  public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
  • CallerRunsPolicy

    由调用者的线程执行该任务

   public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }

类型及使用场景

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

一种线程数量不变以及只有核心线程的线程池,可以快速响应外界的请求

  • newCacheThreadPool
  public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

这个是只有非核心线程,并且最大线程数为Integer.MAX_VALUE,空闲时长60s,适合执行大量的且耗时较少的任务

  • newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

只有一个核心线程的线程池,确保所有的任务都在一个线程中按顺序执行,任务之间不需要处理线程同步的问题

  • newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }

这个是有固定的核心线程而且最大线程数为Integer.MAX_VALUE, 适合执行定时任务和具有固定周期的重复任务

  • newWorkStealingThreadPool
public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

线程池运行时异常处理

  • 直接在run方法中try-catch
  • Submit 方法获取future 对象,try-catch future.get() 方法
  • 重写ThreadPoolExecutor的afterExecute方法,处理传递的异常引用
  • 为工作者线程设置UncaughtExceptionHandler,在uncaughtException方法中处理异常

使用过程需要注意的地方

  • FixedThreadPool和SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,避免堆积大量的请求,从而导致 OOM
  • CachedThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,创建大量的线程,会导致 OOM。
  • 实际应用更多以ThreadPoolExecutor 提供的构造方法去创建线程池,根据实际需求制定核心线程数以及使用有界的工作任务队列。