热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ThreadPoolExecutor源码学习(一)核心属性及应用

在Executors类中,jdk提供了四个线程池可以使用,分别是newCachedThreadPool:corePoolSize是0&#x

在Executors类中,jdk提供了四个线程池可以使用,分别是
newCachedThreadPool:
corePoolSize是0,允许创建的最大线程数量是Integer.MAX_VALUE
newScheduledThreadPool:
允许创建的最大线程数量是Integer.MAX_VALUE
newFixedThreadPool:
任务队列允许的最大长度是Integer.MAX_VALUE
newSingleThreadExecutor:
任务队列允许的最大长度是Integer.MAX_VALUE

这四种是阿里巴巴Java开发规范中所不推荐的,因为这四个线程池中,要么是最大线程数无界,要么阻塞队列无界,可能会造成OOM

推荐使用自定义的线程池,这样可以合理控制最大线程数和阻塞队列以及拒绝策略等

ThreadPoolExecutor中有几个重要的属性


自定义线程池

private static void customThreadPool() {ExecutorService threadPool &#61; new ThreadPoolExecutor(2, 5, 1L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3),Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());try {for (int i &#61; 0; i < 90; i&#43;&#43;) {threadPool.execute(() -> {System.out.println(Thread.currentThread().getName() &#43; "\t 开始处理业务");});}} catch (Exception e) {e.printStackTrace();} finally {threadPool.shutdown();}}

参数说明


核心属性

int corePoolSize&#xff1a;核心线程数
int maximumPoolSize&#xff1a;允许创建的最大线程数
long keepAliveTime&#xff1a;空闲时间
TimeUnit unit&#xff1a;时间单位
BlockingQueue<Runnable> workQueue&#xff1a;任务队列
ThreadFactory threadFactory&#xff1a;线程池工厂
RejectedExecutionHandler handler&#xff1a;拒绝策略对于一个线程池来说&#xff0c;允许有 maximumPoolSize &#43; workQueue.length 个任务提交过来&#xff0c;超过了这个阈值&#xff0c;就会触发拒绝策略private final AtomicInteger ctl &#61; new AtomicInteger(ctlOf(RUNNING, 0));
这个遍历的高三位保存的是线程池的状态&#xff0c;后29位保存的是线程池中工作线程的数量

线程池状态

/*** 运行状态&#xff0c;线程池刚创建就是这个状态* 高三位是111&#xff0c;低29位是0* 该状态会接收新的任务请求* 也会处理在阻塞队列中等待处理的任务*/private static final int RUNNING &#61; -1 << COUNT_BITS;/*** 停工状态&#xff0c;不再接收新任务&#xff0c;但是会处理已经在执行的和阻塞队列中的任务* 高三位为000&#xff0c;低29位为0&#xff0c;* 在running状态调用线程池的shutdown()方法&#xff0c;会从running变更为shutdown*/private static final int SHUTDOWN &#61; 0 << COUNT_BITS;/*** 停止状态&#xff0c;不再接收新任务&#xff0c;已有的任务也会中断,阻塞队列中的任务也不会再运行* 高三位为001&#xff0c;低29位为0* 在调用shutdownnow()方法之后&#xff0c;会从running/shutdown变更为stop状态*/private static final int STOP &#61; 1 << COUNT_BITS;/*** 清空状态&#xff0c;所有任务都停止了&#xff0c;工作的线程也全部结束了&#xff0c;workerCount为0* 高三位为010&#xff0c;低29位为0* 为此状态时&#xff0c;还将调用terminated()方法*/private static final int TIDYING &#61; 2 << COUNT_BITS;/*** 终止状态&#xff0c;线程池已经销毁&#xff1b;为此状态时还将调用terminated()方法* 高三位为100&#xff0c;低29位为0*/private static final int TERMINATED &#61; 3 << COUNT_BITS;

拒绝策略&#xff1a;

1、AbortPolicy&#xff1a;直接抛出异常 RejectedExecutionException
2、CallerRunsPolicy&#xff1a;在调用方线程中执行任务
3、DiscardOldestPolicy&#xff1a;丢弃最先进入到队列中的任务&#xff0c;调用的是queue.offer()方法
4、DiscardPolicy&#xff1a;不做任务处理&#xff0c;其实丢弃的是当前任务

运行原理

1. 创建线程池&#xff0c;等待请求过来2. 当调用了execute()方法添加一个请求任务之后&#xff0c;线程池会做如下判断:3. 1. 如果当前运行的线程数小于corePoolSize&#xff0c;那么会创建线程&#xff0c;运行这个任务2. 如果正在运行的线程数量大于或者等于corePoolSize,那么将这个任务加入队列3. 如果队列也满了&#xff0c;这时候运行的线程数还小于maximumPoolSize&#xff0c;就会创建非核心线程来运行这个任务4. 如果队列满了&#xff0c;且正在运行的线程数达到了maximumPoolSize,那么线程池会启动拒绝策略来执行4. 当一个线程的任务结束之后&#xff0c;会从队列中取下一个任务来执行5. 当一个线程空闲时间超过了keepAliveTime时&#xff0c;线程池会做以下判断:6. 如果当前运行的线程数超过了corePoolSize时&#xff0c;那么这个线程会被销毁&#xff0c;线程池中所有任务结束以后&#xff0c;线程数量会缩减到corePoolSize的大小

execute和submit的区别

1.入参不同&#xff0c;execute的入参只能是Runnable,submit的入参可以是Runnable&#xff0c;也可以是callable
2.submit有返回值&#xff0c;submit底层也是调用的execute&#xff0c;在调用execute的时候&#xff0c;会把入参包装成RunnableFuture
3.submit方便异常处理&#xff0c;如果task抛出了异常&#xff0c;那么通过future.get()可以捕获到异常信息


核心方法

在这里插入图片描述

1.Executor是线程池的基本接口&#xff0c;只有一个execute方法
2.ExecutorService是线程池Executor接口的子接口&#xff0c;额外提供了一些对线程池的其他操作&#xff1a;submit、shutdown等
3.AbstractExecutorService抽象类&#xff0c;实现了executorService的部分接口&#xff0c;比如&#xff1a;submit
4.ThreadPoolExecutor继承了AbstractExecutorService


获取线程池状态、工作线程数量

/*** 获取当前线程池的状态* &#64;param c* &#64;return*/private static int runStateOf(int c) { return c & ~CAPACITY; }/*** 获取线程池活跃的数量* &#64;param c* &#64;return*/private static int workerCountOf(int c) { return c & CAPACITY; }/*** 获取运行状态和活动线程数量的值* &#64;param rs* &#64;param wc* &#64;return*/private static int ctlOf(int rs, int wc) { return rs | wc; }

runWorker()

执行提交的任务&#xff0c;这是比较核心的方法&#xff0c;在该方法中
1、先执行当前提交的任务
2、从任务队列中获取排队的任务
3、并执行


getTask()

这是从任务队列中获取排队任务的方法&#xff0c;在新增一个线程的时候&#xff0c;会先执行当前提交的任务&#xff0c;如果该任务执行完了之后&#xff0c;会从任务队列中尝试获取正在排队的任务&#xff0c;去依次执行


推荐阅读
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 本文整理了Java中com.evernote.android.job.JobRequest.getTransientExtras()方法的一些代码示例,展示了 ... [详细]
author-avatar
zhenhuaYang
编程、骑行、健身、民谣、生活!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有