热门标签 | 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;去依次执行


推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • Spring学习(4):Spring管理对象之间的关联关系
    本文是关于Spring学习的第四篇文章,讲述了Spring框架中管理对象之间的关联关系。文章介绍了MessageService类和MessagePrinter类的实现,并解释了它们之间的关联关系。通过学习本文,读者可以了解Spring框架中对象之间的关联关系的概念和实现方式。 ... [详细]
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
  • 本文讨论了编写可保护的代码的重要性,包括提高代码的可读性、可调试性和直观性。同时介绍了优化代码的方法,如代码格式化、解释函数和提炼函数等。还提到了一些常见的坏代码味道,如不规范的命名、重复代码、过长的函数和参数列表等。最后,介绍了如何处理数据泥团和进行函数重构,以提高代码质量和可维护性。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 本文介绍了在Android开发中使用软引用和弱引用的应用。如果一个对象只具有软引用,那么只有在内存不够的情况下才会被回收,可以用来实现内存敏感的高速缓存;而如果一个对象只具有弱引用,不管内存是否足够,都会被垃圾回收器回收。软引用和弱引用还可以与引用队列联合使用,当被引用的对象被回收时,会将引用加入到关联的引用队列中。软引用和弱引用的根本区别在于生命周期的长短,弱引用的对象可能随时被回收,而软引用的对象只有在内存不够时才会被回收。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
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社区 版权所有