线程池的作用

  1. 降低资源消耗。重复利用已有线程,减少线程的创建和销毁造成的消耗。
  2. 提高响应速度。当有任务需要处理的时候,就不用再花费重新创建线程的时间了。
  3. 提高线程的可管理性。不合理利用线程,会浪费资源,影响系统稳定,线程池可以对线程进行统一分配、调优和监控。

线程池的实现原理

线程池由两种觉得组成:多个工作线程和一个阻塞队列。

  • 工作线程:运行在线程池中,当需要执行任务的时候,就会被用来执行任务。线程池中还会被划分出一个核心线程池。
  • 阻塞队列:当核心线程池中的工作线程数量满了,新加入的任务就会进入阻塞队列。

    执行流程:

线程池使用方法

创建线程池

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, runnableTaskQueue, handler);
  • corePoolsize:线程池的基本大小,通过设置这个值,将线程池的线程数量维持在某一大小。

  • maximumPoolSize:最大线程数量,这个线程池可以运行的线程最大数量。如果阻塞队列满了,而最大线程数量没有满,那么线程池就会创建新的线程来执行任务。如果最大线程数满了,那么就无法执行任务。

  • keepAliveTime:空闲线程的存活时间,如果运行的线程数量超过核心线程数量,同时空闲的线程存活时间大于这个值,那么超时的空闲线程就会被销毁。如果任务很多,每个任务的执行时间很短,就可以调大这个值,以提高线程的利用率。

  • timeUnit:keepAliveTime的单位。

  • runnableTaskQueue:任务队列, 用于保存等待执行任务的阻塞队列,可以有一下几个选择:

    1. ArrayBlockingQueue:基于数组结构的阻塞队列,FIFO;
    2. LinkedBlockingQueue:基于链表,吞吐量高于前者,FIFO;
    3. SynchronousQueue:没有空间的存储队列,每一个插入操作都必须等到其他线程的调用移除,不然插入操作本身处于阻塞。吞吐量高于前者;
    4. PriorityBlockingQueue:具有优先级的无限阻塞队列。
  • handler: 饱和策略,如果实际运行的线程数达到最大值,则执行饱和策略,JDK提供下面4中策略:

    1. AbortPolicy:直接抛出异常,默认策略。
    2. CallerRunsPolicy:只用调用者所在的线程执行任务。
    3. DiscardOldestPolicy:丢弃任务队列中最久的任务。
    4. DiscardPolicy:丢弃新来的任务。

任务提交

提交任务的两种方法:

  • excute() :不需要返回值的任务。可以提交Runnable接口的任务。执行过程中无法抛出异常。

  • submit():需要返回值的。可以提交Callable接口的任务。然会Future对象,通过get获取。

threadsPool.excute(new Runnable() {
@Override
public void run() {
dosometing();
}
}
Future<Object> future = executor.submit(harReturnValuetask);

try{
Object s = future.get();
} catch(InterruptedException e) {
dosomething();
} catch(ExcutionException e) {
dosomething();
} finally {
excutor.shutdown();
}

关闭线程池

两种方法关闭线程,都是通过遍历线程池中的工作线程,然后逐一调用interrupt方法来中断,无法响应中断的线程可能不能关闭。

  • shutdown():将线程池的状态改为关闭,中断没有执行任务的线程和取消阻塞线程中的等待任务,然后等待其他线程执行任务完毕之后才关闭线程。
  • shutdownNow():首先将线程池状态改为关闭,然后停止正在运行或没有运行的任务。

合理配置线程池

主要思想:根据任务的特性来配置线程池的参数。

任务的硬件资源需求

  • CPU密集型任务
    配置尽可能小的线程池,如CPU核心数+1的线程池。这样可以减小切换上下文带来的开销。
  • IO密集型任务
    配置尽可能大的线程池,如CPU核心数*2的线程池。这样当线程任务在等待IO的时候,CPU可以做别的事情了。
  • 混合型任务
    将任务再分为CPU密集和IO密集,然后再用不同的线程池运行,只要两个任务执行的时间相差不是太大,分解后执行的吞吐量将高于串行执行的吞吐量。如果差距很大,时间短的任务会等待时间长的,还要加上任务拆分与合并的开销,更浪费资源。

任务的优先级

利用PriorityBlockingQueue来处理。让优先级高的任务先执行。

任务的执行时间

不同执行时间的任务交给不同规模的线程池,或者交给优先级队列,让时间短的先执行。

任务的依赖性

例如依赖于数据库连接池的任务,需要等待数据库的返回才能继续执行,所以,开多一点的线程数,这样提高CPU的使用效率。

使用有界队列

能增加系统的稳定性和预警能力。要设置大一点,这样在例如SQL操作的时候,可以有足够的线程可以等待,从而也不浪费资源。

Java并发编程的艺术(十)——线程池的更多相关文章

  1. Java并发编程的艺术(十)——线程池(1)

    线程池的作用 减少资源的开销 减少了每次创建线程.销毁线程的开销. 提高响应速度 每次请求到来时,由于线程的创建已经完成,故可以直接执行任务,因此提高了响应速度. 提高线程的可管理性 线程是一种稀缺资 ...

  2. Java并发编程的艺术(十一)——线程池(2)

    Executor两级调度模型 在HotSpot虚拟机中,Java中的线程将会被一一映射为操作系统的线程. 在Java虚拟机层面,用户将多个任务提交给Executor框架,Executor负责分配线程执 ...

  3. Java 并发编程——Executor框架和线程池原理

    Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务 ...

  4. [Java并发编程(二)] 线程池 FixedThreadPool、CachedThreadPool、ForkJoinPool?为后台任务选择合适的 Java executors

    [Java并发编程(二)] 线程池 FixedThreadPool.CachedThreadPool.ForkJoinPool?为后台任务选择合适的 Java executors ... 摘要 Jav ...

  5. [Java并发编程(一)] 线程池 FixedThreadPool vs CachedThreadPool ...

    [Java并发编程(一)] 线程池 FixedThreadPool vs CachedThreadPool ... 摘要 介绍 Java 并发包里的几个主要 ExecutorService . 正文 ...

  6. Java 并发编程——Executor框架和线程池原理

    Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ...

  7. Java并发编程的艺术(六)——线程间的通信

    多条线程之间有时需要数据交互,下面介绍五种线程间数据交互的方式,他们的使用场景各有不同. 1. volatile.synchronized关键字 PS:关于volatile的详细介绍请移步至:Java ...

  8. Java并发编程之深入理解线程池原理及实现

    Java线程池在实际的应用开发中十分广泛.虽然Java1.5之后在JUC包中提供了内置线程池可以拿来就用,但是这之前仍有许多老的应用和系统是需要程序员自己开发的.因此,基于线程池的需求背景.技术要求了 ...

  9. Java并发编程、多线程、线程池…

    <实战java高并发程序设计>源码整理https://github.com/petercao/concurrent-programming/blob/master/README.md Ja ...

随机推荐

  1. peterson算法(软件互斥 转)

    1. 背景        首先,看个例子,进程P1,P2共用一个变量COUNT,初始值为0                                                 因为P1,P ...

  2. 学习一下 Spring Security

    一.Spring Security 1.什么是 Spring Security? (1)基本认识 Spring Security 是基于 Spring 框架,用于解决 Web 应用安全性的 一种方案, ...

  3. mysql 数据库存储路径更改

    使用了VPS一段时间之后发现磁盘空间快满了.本人的VPS在购买的时候买了500gb的磁盘,提供商赠送了20GB的高性能系统磁盘.这样系统就有两个磁盘空间了.在初次安装mysql 的时候将数据库目录安装 ...

  4. python学习--sys.argv

    sys.argv是获取命令行参数的: sys.argv[0]表示代码本身文件路径:从1开始获取参数. import sysprint (sys.argv[0])count = int(sys.argv ...

  5. HW弹药库之红队作战手册

    红方人员实战手册 声明 Author : By klion Date : 2020.2.15 寄语 : 愿 2020 后面的每一天都能一切安好 分享初衷 一来, 旨在为 "攻击" ...

  6. Mysql预处理语句prepare、execute、deallocate

    前言 做CTF题的时候遇到的所以参考资料学习一波.... MySQL的SQL预处理(Prepared) 一.SQL 语句的执行处理 1.即时 SQL 一条 SQL 在 DB 接收到最终执行完毕返回,大 ...

  7. 重闯Sqli-labs关卡第一天(1-4关)

    前言 之前暑假闯了很多关但是最近刷BUGku的题 遇到SQL注入题就凉... 垃圾的我只能继续硬着头皮重新再来学习,再来闯. 第一关:字符型注入 字符型注入就是注入点的数据类型是字符型.字符型注入与数 ...

  8. Jmeter监控插件

    Jmeter-Plugins支持CPU.Memory.Swap.Disk和Network的监控,在测试过程中更加方便进行结果收集和统计分析. 一.准备工作: 1.下载Jmeter-Plugins插件, ...

  9. c# 调用Go 动态库

    [StructLayout(LayoutKind.Sequential)] public struct GoMem { public IntPtr data; public UInt64 len; p ...

  10. MarkDown学习总结-2020.05.11

    1.使用工具 1.1Typora 官网地址:https://www.typora.io/ 下载链接 2.基础入门 注意: []中的内容则是对应格式的标记符,默认全部标识符后面需要多加一个空格才能生效. ...