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是如何实现线程池的。首先看看线程池框架图:

线程池框架

应用与线程池的交互和线程池的内部工作过程如下图所示:

应用与线程池的交互和线程池的内部工作过程

其中有几个重要的概念:

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

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

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);
    }

线程状态流转图如下所示:

线程状态流转图

线程池大小的选择原则

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FreeBuf

某音乐类App评论相关API的分析及SQL注入尝试

涉及工具/包:Fiddler、Burpsuite、Js2Py、Closure Compiler、selenium、phantomjs、sqlmap 摘要: 记录...

25560
来自专栏杨建荣的学习笔记

Greenplum集群问题修复案例

我看了下GP Master端,看到负载并不高,当然这是一个初步的检测,如果集群响应缓慢,则很可能是segment节点上出现了延迟。一看则吓一跳,这是一个segm...

13220
来自专栏一只程序汪的自我修养

使用requirejs编写模块化代码

25850
来自专栏皮振伟的专栏

[linux][memory]内存映射技术分析

前言: KVM的设备虚拟化,除了前文《PIO技术分析》,还有另外一个核心概念---MMIO。原计划这里分析一下KVM的MMIO虚拟化。考虑到MMIO比PIO复杂...

508110
来自专栏社区的朋友们

爬虫实战 : 爬虫之 web 自动化终极杀手(下)

最近写了好几个简单的爬虫,踩了好几个深坑,在这里总结一下,给大家在编写爬虫时候能给点思路。本次爬虫内容有:静态页面的爬取。动态页面的爬取。web 自动化终极爬虫...

1.8K10
来自专栏美团技术团队

美团外卖Android Lint代码检查实践

62250
来自专栏Python小白进阶之旅

还在找远控?来看我用十几行python代码写个简易远程控制

刚开始学习编程的同学可能经常会问:“我学了这个干什么用?买菜的时候掏出电脑来编个程序算算多少钱?”

568130
来自专栏Timhbw博客

『工具』修复windows下各种.dll缺失或者其他组件

2016-03-0413:45:50 发表评论 417℃热度 DirectX修复工具(DirectX Repair)是一款系统级工具软件,简便易用。本程序为绿...

36660
来自专栏散尽浮华

memcached缓存知识简单梳理

memcached工作原理 基本概念:slab,page,chunk。 slab,是一个逻辑概念。它是在启动memcached实例的时候预处理好的,每个slab...

23060
来自专栏公有云大数据平台弹性 MapReduce

EMR之HBASE集群参数调优与压测

HBase 是Hadoop生态里重要一员。对HBase的调优,对节约成本,提升用户体验有重要意义。

34340

扫码关注云+社区

领取腾讯云代金券