java线程池

线程池能够帮助我们提高系统资源利用效率,并简化线程管理。通过并发包下的Executors(不是Executor)可以方便的创建如下几类线程池。分别为:

  • newCachedThreadPool(),用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置的时间超过 60 秒,则被终止并移出缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列
  • newFixedThreadPool(int nThreads),重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有 nThreads 个工作线程是活动的。这意味着,如果任务数量超过了活动线程数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目 nThreads
  • newSingleThreadExecutor(),它的特点在于工作线程数目被限制为 1,操作一个无界的工作队列,所以它保证了所有任务的都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目。
  • newSingleThreadScheduledExecutor() 和 newScheduledThreadPool(int corePoolSize),创建的是个 ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程。
  • newWorkStealingPool(int parallelism),Java 8 才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序

ThreadPoolExecutor源码分析

下面来分析一下ThreadPoolExecutor是如何实现线程池的。首先看看线程池框架图:
java线程池_第1张图片
线程池框架

应用与线程池的交互和线程池的内部工作过程如下图所示:
java线程池_第2张图片
应用与线程池的交互和线程池的内部工作过程

其中有几个重要的概念:
  • 工作队列负责存储用户提交的各个任务,这个工作队列,可以是容量为 0 的 SynchronousQueue(使用 newCachedThreadPool),也可以是像固定大小线程池(newFixedThreadPool)那样使用 LinkedBlockingQueue
  • 内部的“线程池”,这是指保持工作线程的集合(是一个HashSet),线程池需要在运行过程中管理线程创建、销毁。线程池的工作线程被抽象为静态内部类 Worker,基于AQS实现。
  • ThreadFactory 提供上面所需要的创建线程逻辑。
  • 如果任务提交时被拒绝,比如线程池已经处于 SHUTDOWN 状态或者队列已经满了,需要为其提供处理逻辑,Java 标准库提供了类似ThreadPoolExecutor.AbortPolicy等默认实现,也可以按照实际需求自定义。

理解了上面的几个概念,再看ThreadPoolExecutor的构造方法就能够很容易的理解各参数的含义了,源码截图如下:
java线程池_第3张图片
ThreadPoolExecutor构造方法

execute方法的源码如下:

    public void execute(Runnable command) {
//验证传入参数的合法性
        if (command == null)
            throw new NullPointerException();
// 检查工作线程数目,低于 corePoolSize 则添加线程(这里的线程给Worker进行包装)
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
//线程没有被shutdown,则将command加入到任务队列中
        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);
        }
// 尝试添加一个 worker,如果失败以为着已经饱和或者被shutdown了
        else if (!addWorker(command, false))
            reject(command);
    }

线程状态流转图如下所示:
java线程池_第4张图片
线程状态流转图

线程池大小的选择原则

  • 如果是CPU密集型的业务,增加线程数并不能够提高计算能力,反而会因为线程的上下文切换使计算变慢,所以线程数设计成跟CPU的核心数一样是合理的。
  • 如果是等待任务较多的业务,可以通过如下公式进行计算:
    线程数 = CPU 核数 × (1 + 平均等待时间 / 平均工作时间)

你可能感兴趣的:(java线程池)