在之前已经使用过线程池了。在使用中,基本上就是初始化好线程池的实例之后,把任务丢进去,等待调度执行就可以了。使用起来非常简单方便。

new Thread弊端

  • 每次new Thread新建对象,性能差
  • 线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者OOM
  • 缺少更多功能,如更多执行、定期执行、线程中断

线程池的好处

  • 重用存在的线程,减少对象创建、消亡的开销,性能好
  • 可以有效控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞
  • 提供定时执行、定期执行、单线程、并发数控制等高级功能

ThreadPoolExecutor

参数:

  • corePoolSize:核心线程数量。建议和cpu的核心数差不多,当有任务提交,检测当前线程池内的线程数小于corePoolSize的话,新建线程执行任务,而不会开始复用,会新建,直到达到corePoolSize。线程池内的线程数大于等于corePoolSize时,将任务放入workQueue等待。
  • maximumPoolSize:允许线程池内最大线程数。当队列满了之后,如果线程池内的线程数小于maximumPoolSize则新建线程,如果大于等于执行拒绝策略。
    如果maximumPoolSize是30,corePoolSize是10,当队列满了后只能再开20个线程。
  • workQueue:阻塞队列,存储等待执行的任务,很重要,会对线程池运行过程产生重大影响
  • keepAliveTime:线程没有任务执行时最多保持多久时间终止。线程池维护线程所允许的空闲时间。当线程池中的线程数量大于CorePoolSize时,如果这时没有新的任务提交,线程会等待,直到时间超过keepAliveTime才销毁。
  • unit:keepAliveTime的时间单位
  • threadFactory:线程工厂,用来创建线程,会有一个默认的工厂来创建线程。使用默认的工厂创建线程时,线程拥有相同的优先级,并且是非守护的线程,同时也设置了线程的名称。
  • rejectHandle:当拒绝处理任务时的策略。如果workQueue阻塞队列满了,并且没有空闲的线程时,这时还继续提交任务,我们就需要一种策略来处理这个任务。线程池总共提供了四种策略:
    1. 直接抛出异常,默认策略
    2. 用调用者所在的线程执行任务
    3. 丢弃队列中最靠前的任务,并执行当前任务
    4. 直接丢弃这个任务

如果运行的线程数小于CorePoolSize时,直接创建新线程创建任务,即使线程池中的其他线程是空闲的。

如果线程池中的线程数量大于等于CorePoolSize且小于maximumPoolSize时,则只有当wokQueue满时才创建新的线程去处理任务。

如果我们设置CorePoolSize与maximumPoolSize相等,那么创建的线程池大小是固定的,这时如果有新任务提交且workQueue还没满,就把请求放入workQueue中,等待空闲线程从workQueue中取任务进行处理。

如果运行的线程数量大于maximumPoolSize时,这时如果workQueue满,那么会通过一个拒绝策略参数来指定策略处理任务。

如果我们想降低系统资源的消耗,包括CPU的使用率、操作系统资源的消耗,可以设置一个较大的workQueue容量和较小的CorePoolSize容量,这样会降低线程处理的吞吐量。如果我们提交的任务经常发生阻塞,我们可以设置maximumPoolSize来设置线程池容量。如果我们队列容量较小,通常需要把maximumPoolSize设置大一些,这样CPU使用率会高一些。但是如果线程池容量设置过大,在提交人物数量过多的情况下,并发量会增加,那么线程间资源调度就是一个需要考虑的问题,反而会降低处理的吞吐量。

线程池状态

当我们初始化一个线程池之后,通常有上面几种状态。

running:可以接受新提交的任务,也能处理阻塞队列中的任务。

shutdown:关闭状态,当一个线程池实例处于shutdown状态时,不能再接收新提交的任务,但却可以继续处理阻塞队列中已经保存的任务。

stop:也不能接收新的任务,也不处理队列中的任务。会中断正在处理的线程任务。

tidying:所有任务都已经终止了,没有活动中的线程。当线程池进行该状态时候,会执行钩子方法terminated() 。

ThreadPoolExecutor中的方法

  1. execute():提交任务,交给线程池执行
  2. submit():提交任务,能够返回执行结果 相当于execute+Future
  3. shutdown():关闭线程池,等待任务都执行完
  4. shutdownNow():关闭线程池,不等待任务执行完
  5. getTaskCount():线程池已执行和未执行的任务总数
  6. getCompletedTaskCount():已完成的任务数量
  7. getPoolSize():线程池当前的线程数量
  8. getActiveCount():当前线程池中正在执行任务的线程数量

Executors框架接口

  • Executors.newCachedThreadPool:创建一个可缓存的线程池,如果线程池的长度超过了线程的需要,可以灵活回收空闲线程,如果没有回收的,可以新建线程
  • Executors.newFixedThreadPool:创建一个定长的线程池,可以控制线程的最大并发数,超出的线程会在队列中等待
  • Executors.newScheduledThreadPool:也是创建一个定长的线程池。支持定时以及周期性的任务执行。
  • Executors.newSingleThreadExecutor:创建一个单线程的线程池,会用唯一的工作线程来执行任务。保证所有任务按照指定顺序去执行(先入先出等)。

Executors.newCachedThreadPool示例代码:

@Slf4j
public class ThreadPoolExample1 {public static void main(String[] args) {ExecutorService executorService = Executors.newCachedThreadPool();for (int i = 0; i< 10; i++){final int index = i;executorService.execute(new Runnable() {@Overridepublic void run() {log.info("task:{}", index);}});}//一定要关闭线程池,否则程序不结束executorService.shutdown();}
}

Executors.newSingleThreadExecutor示例代码:

@Slf4j
public class ThreadPoolExample3 {public static void main(String[] args) {ExecutorService executorService = Executors.newSingleThreadExecutor();for (int i = 0; i< 10; i++){final int index = i;executorService.execute(new Runnable() {@Overridepublic void run() {log.info("task:{}", index);}});}//一定要关闭线程池,否则程序不结束executorService.shutdown();}
}

输出结果:

17:08:16.870 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:0
17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:1
17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:2
17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:3
17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:4
17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:5
17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:6
17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:7
17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:8
17:08:16.875 [pool-1-thread-1] INFO com.vincent.example.threadPool.ThreadPoolExample3 - task:9

相当于单线程按顺序执行。

Executors.newScheduledThreadPool代码示例:

@Slf4j
public class ThreadPoolExample4 {public static void main(String[] args) {ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);scheduledExecutorService.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {log.warn("schedule run");}},1, 3, TimeUnit.SECONDS);//这里不需要关闭线程池//scheduledExecutorService.shutdown();}
}

上面目的是:延迟1秒后,每个三秒执行一次任务,由于这个任务时不定的执行,因此这里不应该关闭线程池。如果需要关闭线程池的话,可以设置一个触发条件来关闭。类似于Timer类:

@Slf4j
public class ThreadPoolExample4 {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {log.warn("timer run");}}, new Date(), 5*1000);}
}

线程池合理配置

  • CPU密集型任务,就需要尽量压榨CPU,参考值可以设置为NCPU+1
  • 如果是IO密集型任务,参考值可以设置为2*NCPU

我们只用线程池主要是为了重用存在的线程,减少对象创建消亡,能有效控制最大线程并发数,可以避免过多的资源竞争和阻塞,也可以定时执行单线程与控制线程的执行性能比较好。 这不代表线程池应该随时随地用,一定要根据自己的实际场景来分析使用参数配置

java高并发(十八)线程池相关推荐

  1. Java高并发编程:线程池

    这里首先介绍了java5中的并发的小工具包:java.util.concurrent.atomic,然后介绍了线程池的概念,对使用java5的方式创建不同形式的线程进行了演示,之后介绍了两个 对象:C ...

  2. Java高并发系列5-线程池

    Java高并发系列5-线程池 接上一篇Java并发系列4-并发容器我们继续 在编程中经常会使用线程来异步处理任务,但是每个线程的创建和销毁都需要一定的开销.如果每次执行一个任务都需要开个新线程去执行, ...

  3. Java高并发(八)——Thread pool 如何创建及常见并行模式

    在前边 Java高并发(四)--ThreadPool,线程复用 中我们学习了ThreadPool,但是在那篇中我们在create Thread pool的时候,由于我安装了阿里的开发规范插件,一直在警 ...

  4. springcloud工作笔记096---springboot集成多线程_高并发_集成线程池的使用

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 在实际应用中有可能会用到,但是至今用到的还不多 在我们现实开发中肯定会遇到需要延时请求并且高并发的 ...

  5. 高并发的epoll+线程池,epoll在线程池内

    epoll是linux下高并发服务器的完美方案,因为是基于事件触发的,所以比select快的不只是一个数量级. 单线程epoll,触发量可达到15000,但是加上业务后,因为大多数业务都与数据库打交道 ...

  6. 高并发的epoll+线程池,线程池专注实现业务

    分享几个在bilibili听了觉得讲的还不错的几个视频: 1.手写线程池视频:https://www.bilibili.com/video/BV1AT4y13791 2.epoll原理讲解视频:htt ...

  7. Java多线程学习十:线程池实现“线程复用”的原理

    线程复用原理 我们知道线程池会使用固定数量或可变数量的线程来执行任务,但无论是固定数量或可变数量的线程,其线程数量都远远小于任务数量,面对这种情况线程池可以通过线程复用让同一个线程去执行不同的任务,那 ...

  8. 《Java 高并发》05 线程的基本操作

    volatile 与 Java 内存模型 Java 内存模型都是围绕着原子性.有序性和可见性展开的.为了在适当的场合,确保线程间的原子性.有序性和可见性.Java 使用了一些特许的操作或者关键字来申明 ...

  9. 《Java 高并发》03 线程的生命周期

    相关概念 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程. 一个进程是一个独立的运行环境,它可以被看作一个程序或者一个应用.而线程是在进程中执行的一个 ...

  10. Java高并发编程:线程锁技术

    目录 1 什么是线程锁 2 synchronized 1. 对象锁 2. 修饰对象方法 3. 类锁 4. 对象锁和类锁 5. 卖火车票示例 6. 生产一个消费一个示例 3 Lock 3.1 重入锁 R ...

最新文章

  1. 【牛客网】最长对称子串
  2. 路由及路由器工作原理深入解析3:路由与port
  3. [编写高质量代码:改善java程序的151个建议]建议69 列表相等只需关心元素相等...
  4. HTTP API 设计指南(基础部分)
  5. JS数组去重之利用set数据结构去重
  6. 论c++/java/c 与python的语法上的区别
  7. 【⛔拒绝内卷,学会shell,走遍天下都不怕⛔】shell编程基础
  8. 程序结束后去哪儿了?
  9. Java高级语法笔记-HashMap
  10. 阿里云 OSS 如何设置防盗链, 上个月图床流量耗费50G+,请求次数10W+,什么鬼?
  11. 第九课 如何调试以太坊官网的智能合约众筹案例
  12. WORD图标显示为白色解决办法
  13. hard link and symbolic link
  14. 行人重识别实验笔记3-JDAI fast-reid项目配置
  15. python savefig函数_python中savefig中的键事件。怎么做?
  16. 说说技术总监的三板斧(十年肺腑之言)
  17. java 读取小数位数_java如何获取一个double的小数位数
  18. 一键GHOST光盘版官方版
  19. container 和initContainers使用
  20. linux考试中的7654_Excel工作表G8单元格的值为7654.375,执行某些操作之后,在G8单元格中显示一串“”符号,说明G8单元格的()...

热门文章

  1. Excel 数据有效性,怎么提示指定的命名区域不存在?
  2. php中if的效率,PHP中if和or运行效率对比
  3. redis管道pipeline的运用
  4. Bringing up interface eth2: Error: No suitable device found: no device found for connection 'System
  5. Linux的cmake3的安装 cmake3编译安装成功了的 yum对于cmake3表示成功但实际没成功
  6. qt geomery的单位是什么_斜管沉淀池的原理是什么?
  7. 慢查询工具percona安装
  8. cdr怎么转换成psd转换为位图标准_动漫角色转换真人,飞屋环游记中的小罗竟酷似他……...
  9. Apache下如何禁止指定目录运行PHP脚本
  10. 闲置硬盘自制nas私有云_闲置U盘不用扔,教你一招变云盘,随时随地备份数据、访问私有云...