java多线程系列16 线程池
当系统系统规模较小,我们可以不使用线程池。但是当系统到达一定规模,频繁的创建和销毁线程池会消耗很多资源。
合理利用线程池能够带来三个好处。
1降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
2提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
3提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
下面演示下线程池的基本的使用
public class ThreadPoolExecutorTest {
	public static void main(String[] args) {
		ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
				new LinkedBlockingQueue<Runnable>());
		Runnable runnable = null;
		for (int i = 0; i < 20; i++) {
			runnable = new Runnable() {
				@Override
				public void run() {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + " run");
				}
			};
			executor.execute(runnable);
		}
		executor.shutdown();
	}
}
Jdk默认了实现了4种线程池。
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,默认是60秒,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
查看源码 ,都是调用

其本质是通过不同的参数初始化一个ThreadPoolExecutor对象,只是传递的参数不同罢了
具体参数解释如下:
corePoolSize 线程池中的核心线程数,
maximumPoolSize 线程池中允许的最大线程数
keepAliveTime 线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间
keepAliveTime的 单位 unit
workQueue
用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口,在JDK中提供了如下阻塞队列:
ArrayBlockingQueue:基于数组的有界阻塞队列,按FIFO排序任务;
LinkedBlockingQuene:基于链表的无界阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
priorityBlockingQuene:具有优先级的无界阻塞队列;
handler
线程池的拒绝策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池内置了4种策略:
1、AbortPolicy:直接抛出异常,默认策略;
2、CallerRunsPolicy:用调用者所在的线程来执行任务;
3、DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
4、DiscardPolicy:直接丢弃任务;
当然我们也可以根据应用场景实现RejectedExecutionHandler接口,自定义拒绝策略,
实际开发中,一般先记录日志 再开一个定时去处理
执行流程图如下

======================================================================
原理解析
找到线程池核心的execute()方法 源代码如下
if (command == null)
throw new NullPointerException();
//ctl是一个包装变量 包含了线程池的状态以及线程池中线程数
int c = ctl.get();
//workerCountOf 获取线程池的当前线程数
if (workerCountOf(c) < corePoolSize) { // 【步骤1】
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) { // 【步骤2】
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command)) //【步骤4】
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false)) // 【步骤3】
reject(command);
}
具体的执行流程如下:
--> 如果线程数小于corePoolSize,则执行addWorker方法创建新的线程执行任务 并返回 ;否则执行步骤2;
--> 如果线程池处于RUNNING状态,并且任务成功放入阻塞队列中,则执行步骤4,否则执行 步骤3
--> 再次检查线程池的状态,如果线程池没有RUNNING,并且从阻塞队列中删除任务,则执行reject方法处理任务;
--> 执行addWorker方法创建新的线程执行任务,如果addWoker执行失败,则执行reject方法处理任务;
接下来我们看看addWorker方法
addWorker分为2部分
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false; for (;;) {
int wc = workerCountOf(c);
//通过自旋的方式,判断要添加的Worker是否是true,如果是的话,那么
//则判断当前的workerCount是否大于corePoolsize,否则则判断是否大于//maximumPoolSize,如果满足的话,说明workerCount超出了线程池大小,直//接返回false 如果没有超出就cas让workerCount+1 如果成功就跳出循环 失//败就继续进行状态的判断
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
--------第一部分完
/*如果满足了的话,那么则创建一个新的Worker对象 满足状态就添加到works中 然后启动Worker中的线程开始执行任务*/
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
java多线程系列16 线程池的更多相关文章
- Java多线程系列--“JUC线程池”06之 Callable和Future
		
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
 - Java多线程系列--“JUC线程池”02之 线程池原理(一)
		
概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...
 - Java多线程系列--“JUC线程池”03之 线程池原理(二)
		
概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...
 - Java多线程系列--“JUC线程池”04之 线程池原理(三)
		
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...
 - Java多线程系列--“JUC线程池”05之 线程池原理(四)
		
概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...
 - Java多线程系列--“JUC线程池”01之 线程池架构
		
概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容——线程池.内容包括:线程池架构 ...
 - java多线程系列(六)---线程池原理及其使用
		
线程池 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 java多线程系列(三)之等待通知 ...
 - Java多线程系列  JUC线程池04   线程池原理解析(三)
		
转载 http://www.cnblogs.com/skywang12345/p/3509954.html https://blog.csdn.net/qq_22929803/article/det ...
 - Java多线程系列 JUC线程池06 线程池原理解析(五)
		
ScheduledThreadPoolExecutor解析 ScheduledThreadPoolExecutor适用于延时执行,或者周期性执行的任务调度,ScheduledThreadPoolExe ...
 
随机推荐
- 把已经安装到C盘的软件完美移动到D盘
			
背景信息 今天早上在安装软件的时候发现C盘爆满,只剩下最后10G了.而我要安装的玩意儿必须装到C盘. 靠清理垃圾文件来解决并不是一个好方法,实际上通常垃圾文件占用很少,而且就算清理了,也还会再出现. ...
 - Java IO浅析
			
1.File类 /** * * @author lenovo * * File类 * pathSeparator * separator * * File() * boolean createNewF ...
 - C语言 链表(Dev C++/分文件版)
			
头文件:quechain.h struct Question { int _id; struct Question* pre; struct Question* next; }; void chain ...
 - WHERE 子句操作符
			
操作符(operator) 用来联结或改变WHERE子句中得子句的关键字,也称为逻辑操作符(logical operator): 操作符 说 明 = 等于 <> 不等于 != 不等于 & ...
 - Python有趣时刻,这些代码让你大呼"卧槽,怎么会这样"
			
分享一个实用问题,用python读取Excel并保存字典,如何做? 下面是该同学问题截图和代码 image.png 代码截图是下面这样的 image.png 不知道大家第一眼看了这个代码,什么感受?我 ...
 - eclipse的安装和汉化
			
下载 打开浏览器输入网址 www.eclipse.org/ ,回车( Enter ) 点击下载( DOWNLOAD ) 会进入一个新的页面,点击下载软件包(Download Packages) 找到对 ...
 - HBuilder ,及自用主题
			
字体:Consolas http://bbs.csdn.net/topics/390858585 让代码更美:你最爱的编程字体 http://www.dcloud.io HBuilder下载 htt ...
 - Android下的几种时间格式转换
			
更多更全的工具类,请参考github上的Blankj/AndroidUtilCode 将毫秒转换为小时:分钟:秒格式 public static String ms2HMS(int _ms){ Str ...
 - 201772020113 李清华《面向对象程序设计(java)》第三周学习总结
			
一.测试题反思: 这次的测试题暴露出我在学习上的很多问题:首先,编程能力非常薄弱,编程题目只写出了第一个程序,还因为小问题通不过测试,以后一定要多上手练习,多阅读示例程序.其次,对理论知识的掌握不全面 ...
 - Spring用了哪些设计模式
			
单例:只产生一个对象,共享对象的资源: 多例:产生多个对象,对象资源没有联系:(action) 在ssm框架中 service层.dao层.controller层都是默认使用单例模式,只会产生唯一 一 ...