浅析Java中的线程池
Java中的线程池
几乎所有需要异步或并发执行任务的程序都可以使用线程池,开发过程中合理使用线程池能够带来以下三个好处:
- 降低资源消耗
 - 提高响应速度
 - 提高线程的可管理性
 
1. 线程池的实现原理
当我们提交一个新任务到线程池时,线程池的处理流程如下:


其中,任何创建新线程的操作都需要获取全局锁。
ThreadPoolExecutor采取上述步骤的设计思路,是为了在执行execute()方法时,尽可能地避免获取全局锁。在ThreadPoolExecutor完成预热后,几乎所有的execute()方法都是执行步骤二,而步骤二不需要获取全局锁。
工作线程在完成当前的任务后,会自己到工作队列中循环获取任务来执行:

2. 线程池的使用
2.1 创建线程池
我们通过ThreadPoolExecutor来创建线程池。
new ThreadPoolExecutor(corePoolSize // 核心线程池大小
                      ,maximumPoolSize // 线程池大小
                      ,keepAliveTime // 最长存活时间
                      ,milliseconds // 时间单位
                      ,runnableTaskQueue // 工作队列
                      ,handler) // 饱和策略
corePoolSize 参数为线程池基本大小,即核心线程池的最大容量。当核心线程池未满时,只要收到新任务都会创建一个新的线程。当核心线程池满且无空闲工作线程时,会将任务存到任务队列中。
**maximumPoolSize **参数为线程池最大数量,即线程池允许创建的最大线程数。如果任务队列满了,将会创建新线程来执行任务,直到线程数量达到该最大数量。值得注意的是如果使用了无界的任务队列,那么这个参数就没有效果。
keepAliveTime 参数为空闲线程存活时间。
**TimeUnit **参数为线程活动保持时间的单位。
**workQueue **参数为任务队列,它是一个用于保存等待执行的任务的阻塞队列,可以选择以下几个。
队列 底层 是否有界 其他 ArrayBlockingQueue 数组· 有界 FIFO LinkedBlockingQueue 链表 无界 FIFO SynchronousQueue 不存储元素 有界 每个插入操作都必须等到另一个线程调用移除操作 PriorityBlockingQueue 数组 无界 有优先级 ThreadFactor参数为线程工厂。可以通过线程工厂为每个创建出来的线程设置更有意义的名字。
RejectedExecutionHandler参数为饱和策略。当队列和线程池都满了,我们需要使用饱和策略来处理新任务。一般有以下四种策略。
AbortPolicy:直接抛出异常。CallerRunsPolicy:只用调用者所在线程来运行任务。DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。DiscardPolicy:不处理,丢掉。- 当然,也可以通过
RejectedExecutionHandler接口自定义策略,比如记录日志或者持久化存储不能处理的任务。 

为什么要求用 ThreadPoolExecutor 创建线程池
否则有资源耗尽的风险,Executors返回的线程池对象弊端如下:
FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量请求,导致 OOM。CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为Integer.MAx_VALUE,可能会创建大量线程,导致 OOM。
2.2 向线程池提交任务
可以用execute()和submit()两个方法向线程池提交任务,前者提交不需要返回值的任务,后者提交需要返回值的任务。
2.3 关闭线程池
使用shutdown()或shutdownNow()方法来关闭线程池。原理是遍历线程池中的工作线程,逐个调用interrupt()方法。
shutdownNow()会将线程池状态设置为STOP,并尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。而shutdown()只是将线程池状态设置为SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
通常调用shutdown()方法,如果任务不一定要执行完,就调用shutdownNow()。
3. 合理配置线程池
我们需要通过任务的特性,来分析合理的线程池配置方式:
- 任务的性质:CPU 密集型任务、IO 密集型任务和混合型任务
- CPU 密集型任务配置尽可能少的线程,如配置 CPU 核数+ 1 个线程。
 - IO 密集型人物应配置尽可能多的线程,如配置 2 * cpu 核数个线程。
 - 混合型任务尽量拆分
 
 - 任务优先级:高、中、低
- 优先级不同的任务可以使用优先级队列
PriorityBlockingQueue来处理。 
 - 优先级不同的任务可以使用优先级队列
 - 任务执行时间:长、中、短
- 执行时间不同的任务交给不同规模的线程池处理。
 
 - 任务依赖性:是否以来其他系统资源,如数据库资源
- 依赖数据库连接池的任务,因为线程提交 SQL 后需要等待数据库返回结果,等待时间越长则 CPU 空闲时间越长,那么线程数应该设置得越大,才能更好地使用 CPU。
 
 
浅析Java中的线程池的更多相关文章
- 【万字图文-原创】 | 学会Java中的线程池,这一篇也许就够了!
		
碎碎念 关于JDK源码相关的文章这已经是第四篇了,原创不易,粉丝从几十人到昨天的666人,真的很感谢之前帮我转发文章的一些朋友们. 从16年开始写技术文章,到现在博客园已经发表了222篇文章,大多数都 ...
 - 《Java并发编程的艺术》 第9章 Java中的线程池
		
第9章 Java中的线程池 在开发过程中,合理地使用线程池能带来3个好处: 降低资源消耗.通过重复利用已创建的线程 降低线程创建和销毁造成的消耗. 提高响应速度.当任务到达时,任务可以不需要等到线程创 ...
 - JAVA中创建线程池的五种方法及比较
		
之前写过JAVA中创建线程的三种方法及比较.这次来说说线程池. JAVA中创建线程池主要有两类方法,一类是通过Executors工厂类提供的方法,该类提供了4种不同的线程池可供使用.另一类是通过Thr ...
 - Java中的线程池用过吧?来说说你是怎么理解线程池吧?
		
前言 Java中的线程池用过吧?来说说你是怎么使用线程池的?这句话在面试过程中遇到过好几次了.我甚至这次标题都想写成[Java八股文之线程池],但是有点太俗套了.虽然,线程池是一个已经被说烂的知识点了 ...
 - 第9章 Java中的线程池 第10章 Exector框架
		
与新建线程池相比线程池的优点 线程池的分类 ThreadPoolExector参数.执行过程.存储方式 阻塞队列 拒绝策略 10.1 Exector框架简介 10.1.1 Executor框架的两级调 ...
 - java 中的线程池
		
1.实现下面的一个需求,控制一个执行函数只能被五个线程访问 package www.weiyuan.test; public class Test { public static void main( ...
 - Java中的线程池
		
package com.cn.gbx; import java.util.Date; import java.util.Random; import java.util.Timer; import j ...
 - java 中的线程池和线程 调用小demo
		
public class Main { public static void main(String[] args) { try { /// ThreadPoolExecutor executor = ...
 - Java中的线程池ExecutorService
		
示例 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.u ...
 
随机推荐
- IDEA报错 Unable to open debugger port (127.0.0.1:63342): java.net.BindException  "Address already in use
			
Unable to open debugger port (127.0.0.1:63342): java.net.BindException "Address already in use ...
 - nim_duilib之msgbox用法(23)
			
概述 本文将介绍 msgbox 的用法 更多用法,请参考 源码 改进了原有的xml样式 一个样式 xml结构 整体垂直布局 xml源码 demo源码下的msg/msg.xml文件内容 改为如下 注意: ...
 - 【LeetCode】747. Largest Number At Least Twice of Others 解题报告(Python)
			
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 寻找两次最大值 排序 大顶堆 日期 题目地址:htt ...
 - E. Number With The Given Amount Of Divisors
			
E. Number With The Given Amount Of Divisors time limit per test 2 seconds memory limit per test 256 ...
 - UVA11754 - Code Feat
			
Hooray! Agent Bauer has shot the terrorists, blown upthe bad guy base, saved the hostages, exposed ...
 - sql注入 —sqli-labs/Less-5 逻辑注入
			
逻辑注入: 1.尝试查询数据库: (1)判断数据长度 -- 构造逻辑判断:and length(database())=8 当长度等于"8 "时回显正常,所以数据库长度为" ...
 - Go package(3):io包介绍和使用
			
IO 操作的基本分类 在计算机中,处理文件和网络通讯等,都需要进行 IO 操作,IO 即是 input/ouput,计算机的输入输出操作. Go语言中的 IO 操作封装在如下几个包中: io 为 IO ...
 - Cython编译动态库、引用C/C++文件
			
将某些.py 编译成动态库 设置好要编译的module们: compile_to_c_modules = [ 'package.module' ] 将它们转换成cythonize可识别的参数: def ...
 - Android开发 SeekBar(拖动条)的使用
			
SeekBar是Progress的子类,Progress主要用来显示进度,但是不能和用户互动,而SeekBar则可以供用户进行拖动改变进度值 实现拖动进度条并显示在文本中: <?xml vers ...
 - 【Redhat系列linux防火墙工具】firewalld与iptables防火墙工具的激烈碰撞
			
前言 iptables与firewalld防火墙管理工具在linux发行版Redhat7系列使用较为广泛. UFW则是在linux发行版Ubuntu下进行管理防火墙的一款管理工具. 在选用防火墙工具的 ...