java多线程中,线程池的最上层接口是Executor,ExecutorService实现了Executor,是真正的管理线程池的接口,ThreadPoolExecutor间接继承了ExecutorService,提供了多种具体的线程池实现,在日常开发中一般直接使用Executors工具类提供的几种常用ThreadPoolExecutor,下面详细介绍下ThreadPoolExecutor.

ThreadPoolExecutor基本参数

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 1000L, 
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(20),
new ThreadPoolExecutor.CallerRunsPolicy());

  corePoolSize:核心线程数的大小,也有说线程池最小线程数

  maximumPoolSize:线程池最大线程数

  keepAliveTime:当没有任务执行时,线程能存活的最大时间,这里说的线程是指大于corePoolSize,小于maximumPoolSize的线程

  timeunit:keepAliveTime的时间单位

  workQueue:用来存放task的堵塞队列,队列的选择和size的大小对线程池的运行有直接影响,默认有几种实现,后面详说.

  rejectHandler:拒绝策略,当队列已满并且线程数达到maximumPoolSize时,再有新任务进来时会执行拒绝策略,默认集中实现,后面详说.

ThreadPool模型初始化

  ThreadPool线程池初始化时,不会创建corePoolSIze的线程,也就是说在没有task进来的时候,线程池是空的,当有task进来的时候,开始创建线程,并且线程执行完task后不会销毁,而是驻留内存,直至达到corePoolSize,那什么时候线程数会再度增加达到maxPoolSize呢,这就取决于存放task的queue的size了,如果task的数量一直不超过指定的size那么就不会创建新的线程出来,反之,则会创建新的线程去执行task,那么新建出来的线程执行完task也会一直驻留内存吗?答案是不会,这时候就要看设置的keepAliveTime,如果在超过了这个时间后还是没有task去使用这个线程,则线程销毁,直至线程数等于corePoolSize.那么如果queue也满了,线程数也达到maxPoolsize,这时候怎么办呢,这时rejectHandler就会发挥作用,会根据我们指定的拒绝策略去处理这种场景.

Executors的几个默认实现

public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}

  1.newSingleThreadExecutor:创建一个单线程的线程池.如果这个线程在执行霍城中因为异常结束,则会创建一个新的线程来代替它,这个线程池保证所有任务的执行顺序是按照任务提交顺序进行.

 public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}

  2.newFixedThreadPool:创建一个固定大小的线程池.看源码可知,该实现创建了一个corePoolSize等于maximumPoolSize的线程池.

public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}

  3.newCachedThreadPool:创建一个可缓存的线程池.看源码可知corePoolSize=0,即当线程空闲时会被回收,当线程忙碌时又可以"源源不断"的创建新线程来执行task.

  默认线程池实现中还有ScheduledThreadPoolExecutor,可以实现定时的一些功能,可用来代替Timer或者TimeTask.这里不再展开说.

线程池的堵塞队列

  如何选择线程池的堵塞队列,取决我们的业务场景,即我们希望以一种什么样的排队策略来处理任务.排队通常有3种策略,对应下面几种queue.

  直接提交(SynchronousQueue):不排队,这个队列不会存储task,会将调用方的task直接提交给线程,指定了SynchronousQueue的线程池通常会把maximumPoolSize配置的比较大,否则可能会导致没有足够的线程来执行task,而导致task无法放入queue而被丢弃或拒绝.

  有界队列(ArrayBlockingQueue):通过指定ArrayBlockingQueue的size可以设置队列的最大存储个数,当超出这个个数时就会新建线程去执行task直至线程数达到maximumPoolSize,有界队列size的设置和maximumPoolSize的设置息息相关.会影响CPU的使用率以及系统吞吐量.

  无界队列(不设定size的LinkedBlockingQueue):当线程数达到corePoolSize的仍有task进来时,会源源不断进队列,由于无解,maximumPoolSize参数会失效,线程数最大只能达到corPoolSize.

线程池拒绝策略

  CallerRunsPolicy:使用调用方的线程来执行task,通常情况下调用方线程就是指我们所说的主线程,这样的好处是不会丢弃task,但是缺点也很明显,使用这种策略会堵塞主线程,进而拖慢主线程的整个调用时长.而基于此,该策略同时也减缓了新的task提交进来的速度(因为主线程本来只需要用来提交task就好了,现在直接去执行task,后面的task进来的速度就慢了).

  AbortPolicy:这个策略简单粗暴,直接抛出异常,不跟你多BB,需要注意的是,这个是jdk的默认策略.

  DiscardPolicy:这个和AbortPolicy差不多,区别是不会抛出异常,直接丢弃.

  DiscardOldestPolicy:这也是一种丢弃策略,不过和上面的DiscardPolicy刚好相反,她丢弃的不是新进来的task而是在堵塞队列中存在时间最久的那个task,即丢弃最早进入队列并且还没有被执行的task.

多线程学习笔记-深入理解ThreadPoolExecutor的更多相关文章

  1. java多线程学习笔记——详细

    一.线程类  1.新建状态(New):新创建了一个线程对象.        2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中, ...

  2. 多线程学习笔记九之ThreadLocal

    目录 多线程学习笔记九之ThreadLocal 简介 类结构 源码分析 ThreadLocalMap set(T value) get() remove() 为什么ThreadLocalMap的键是W ...

  3. JAVA多线程学习笔记(1)

    JAVA多线程学习笔记(1) 由于笔者使用markdown格式书写,后续copy到blog可能存在格式不美观的问题,本文的.mk文件已经上传到个人的github,会进行同步更新.github传送门 一 ...

  4. java进阶-多线程学习笔记

    多线程学习笔记 1.什么是线程 操作系统中 打开一个程序就是一个进程 一个进程可以创建多个线程 现在系统中 系统调度的最小单元是线程 2.多线程有什么用? 发挥多核CPU的优势 如果使用多线程 将计算 ...

  5. 微信小程序开发:学习笔记[7]——理解小程序的宿主环境

    微信小程序开发:学习笔记[7]——理解小程序的宿主环境 渲染层与逻辑层 小程序的运行环境分成渲染层和逻辑层. 程序构造器

  6. Java多线程学习笔记(一)——多线程实现和安全问题

    1. 线程.进程.多线程: 进程是正在执行的程序,线程是进程中的代码执行,多线程就是在一个进程中有多个线程同时执行不同的任务,就像QQ,既可以开视频,又可以同时打字聊天. 2.线程的特点: 1.运行任 ...

  7. java 多线程学习笔记

    这篇文章主要是个人的学习笔记,是以例子来驱动的,加深自己对多线程的理解. 一:实现多线程的两种方法 1.继承Thread class MyThread1 extends Thread{ public ...

  8. Java多线程学习笔记--生产消费者模式

    实际开发中,我们经常会接触到生产消费者模型,如:Android的Looper相应handler处理UI操作,Socket通信的响应过程.数据缓冲区在文件读写应用等.强大的模型框架,鉴于本人水平有限目前 ...

  9. Java多线程学习笔记

    进程:正在执行中的程序,其实是应用程序在内存中运行的那片空间.(只负责空间分配) 线程:进程中的一个执行单元,负责进程汇总的程序的运行,一个进程当中至少要有一个线程. 多线程:一个进程中时可以有多个线 ...

随机推荐

  1. JDK源码分析(6)之 LinkedHashMap 相关

    LinkedHashMap实质是HashMap+LinkedList,提供了顺序访问的功能:所以在看这篇博客之前最好先看一下我之前的两篇博客,HashMap 相关 和 LinkedList 相关: 一 ...

  2. github总结(4)--关于git reset --hard这个命令的惨痛教训

    背景叙述: 前几天,上传自己的个站到git上的时候,手欠脑发晕的用了次git reset --hard xxxxxx 命令.由于只在线上传入了一个index.html页面(自己都不知道自己咋想的,就这 ...

  3. NFS的安装

    NFS的安装鸟哥地址:http://vbird.dic.ksu.edu.tw/linux_server/0330nfs_2.php 13.2 NFS Server 端的设定 既然要使用 NFS 的话, ...

  4. 【.NET Core项目实战-统一认证平台】第十章 授权篇-客户端授权

    [.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章介绍了如何使用Dapper持久化IdentityServer4(以下简称ids4)的信息,并实现了sqlserver和mysql两种 ...

  5. 【转载】网站服务器运维记实:阿里云1核2G突发性能t5服务器突然变得卡顿

    阿里云突发性能服务器1核2G的t5服务器在高资源利用率的情况下运行一段时间后,发现服务器反应变得很慢,通过windows远程桌面连接上服务器后查看到CPU性能一直在90%到100%之间,无法降下来.前 ...

  6. 详解原生JS回到顶部

    第一次知道可以用超链接(锚点)实现回到顶部的时候我是很震惊的,想着原来可以这么简单啊!但是作为一个正在学习JS的小白,不能就这么轻易止步,所以查资料自己整了一个JS返回顶部. HTML代码: ? 1 ...

  7. Array的 map() 和 reduce()

    map() map() 方法返回一个新数组,新数组中的元素为原始数组中的元素依次调用参数中的函数处理后的值. map() 方法不会对空数组进行检测,也不会修改原数组. 语法: array.map(fu ...

  8. 荣耀MagicBook黑苹果(i7)High Sierra 10.13.6

    这里有大佬维护的新版本EFI,对应10.14.4:https://github.com/hjmmc/Honor-Magicbook ---------------------------------- ...

  9. Dynamics 365设置错误通知首选项的方法

    本人微信公众号:微软动态CRM专家罗勇 ,回复288或者20181205可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . 使用 ...

  10. postgresql 日志报错could not write to log file: No space left on device,could not write lock file "postmaster.pid": No space left on device

    今天遇到了一个特别奇怪的问题,我在用docker容器的时候,发现我的postgresql怎么也启动不起来 尝试了N多种办法,最后看了看postgresql的日志发现 postgresql 日志中报错 ...