你所了解的Java线程池
在jvm中,线程是一个宝贵的资源,创建与销毁都会抢占宝贵的内存资源,为了有效的重用线程,我们用线程池来管理线程,让创建的线程进行复用。
JDK提供了一套Executor框架,帮助我们管理线程,核心成员如下:
它们都在java.util.concurrent包中,是JDK并发包的核心类,其中,Executor是一个interface,只有一个execute方法;Executors扮演着线程工厂的角色;ThreadPoolExecutor表示一个线程池,用来管理我们的线程。
ThreadPoolExecutor通用的构造函数如下:
public ThreadPoolExecutor(
int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit timeUnit,
BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, workQueue);
}
corePoolSize:线程池基本大小;maximumPoolSize:线程池最大大小;keepAliveTime:线程的存活时间;timeUnit:线程活动保持的时间单位;workQueue:工作队列;
说明:BlockingQueue,可以称之为共享队列,后续有专门的文章来介绍。
线程池工作原理:
1.线程池判断核心线程池中线程是否都在工作,如果不是,则创建一个新的工作线程来执行任务,如果核心线程池中的线程都在执行任务,则进入下一步;
2.线程池会判断工作线程是否已经满了,如果工作队列没有满,则将新提交的任务存储在工作队列中等待执行,如果队列满了,则进入下一步;
3.线程池判断线程池(这里判断的是否到达maximumPoolSize)是否都处于执行状态,如果没有的话,则创建一个新的线程来执行,如果满了,则交给饱和策略来处理这个任务,默 认是抛出异常(AbortPolicy),也可以自定义饱和策略。
现在来看一下exwcute方法:
public void execute(Runnable var1) {
if(var1 == null) {
//如果没有任务,直接抛出异常
throw new NullPointerException();
} else {
//ct1表示一个AtomicInteger,是一个原子类,非阻塞的,相比与synchronzied与锁,性能更好,后续文章会详细介绍,这里的含义表示在当前的线程数
int var2 = this.ctl.get();
// 创建一个新的线程,并把当前任务 command 作为这个线程的第一个任务(firstTask)
if(workerCountOf(var2) < this.corePoolSize) {
//执行的结果会包装为一个FutureTask返回,后续有文章介绍异步(Future)
if(this.addWorker(var1, true)) {
//return直接返回,保证线程池的高可用,其他的任务交给addWorker()去执行,线程池去处理下一个任务。
return;
}
var2 = this.ctl.get();
} //执行到这里,要么是因为,达到核心线程数,要么是因为,doWorker()执行失败,没有走return
//首先会判断线程池的运行状态,然后把该任务放入到工作队列中
if(isRunning(var2) && this.workQueue.offer(var1)) {
int var3 = this.ctl.get();
//如果线程池不在运行状态,移除该任务,并且执行拒绝策略
if(!isRunning(var3) && this.remove(var1)) {
this.reject(var1);
} else if(workerCountOf(var3) == 0) {
//否则执行该任务
this.addWorker((Runnable)null, false);
}
//进入这里表示上面的1,2都失败了,在maximumPoolSize来创建线程执行任务,如果失败,则执行决绝策略。
} else if(!this.addWorker(var1, false)) {
this.reject(var1);
} }
}
然后我们来看一下,上面多次调用的doWorker()方法:
//两个参数,分别表示任务与判断是否进入的分支(true为corePoolSize,false为maximumPoolSize)
private boolean addWorker(Runnable var1, boolean var2) {
while(true) {
//上面讲述过
int var3 = this.ctl.get();
int var4 = runStateOf(var3);
// 如果线程池已关闭,并满足以下条件之一,那么不创建新的 worker:
//var4>0表示,线程池已经关闭
//若果任务不存在或者工作队列为空或者线程池正在关闭等,表示不执行该任务
if(var4 >= 0 && (var4 != 0 || var1 != null || this.workQueue.isEmpty())) {
return false;
} while(true) {
int var5 = workerCountOf(var3);
if(var5 >= 536870911 || var5 >= (var2?this.corePoolSize:this.maximumPoolSize)) {
//var2通过true或者false来判断当前线程池的状况,大于corePoolSize或者maximumPoolSize,则决绝执行。
return false;
}
//成功则创建线程执行任务
//compareIncreamentWorkerCount()该方法表示原子操作,底层调用的是AtomicInteger.compareAndSet(int var1,int var2),表示当前值为var1,则设置为var2,后续会详细介绍
//如果失败,说明不止有一个线程同时操作此方法,创建线程。
if(this.compareAndIncrementWorkerCount(var3)) {
// worker 是否已经启动
boolean var18 = false;
/ 是否已将这个 worker 添加到 workers 这个 HashSet 中
boolean var19 = false;
ThreadPoolExecutor.Worker var20 = null; try {
//创建线程执行任务
var20 = new ThreadPoolExecutor.Worker(var1);
//Worker的构造方法会调用 ThreadFactory 来创建一个新的线程
Thread var6 = var20.thread;
if(var6 != null) {
//重入锁,锁住下列操作,保证原子性,保证在操作过程中,线程池不会被关闭
ReentrantLock var7 = this.mainLock;
var7.lock(); try {
int var8 = runStateOf(this.ctl.get());
if(var8 < 0 || var8 == 0 && var1 == null) {
if(var6.isAlive()) {
throw new IllegalThreadStateException();
}
//增加到Set集合中。用于执行
this.workers.add(var20);
int var9 = this.workers.size();
//因为 workers 是不断增加减少的,通过这个值可以知道线程池的大小曾经达到的最大值
if(var9 > this.largestPoolSize) {
this.largestPoolSize = var9;
} var19 = true;
}
} finally {
var7.unlock();
} if(var19) {
//启动线程执行
var6.start();
var18 = true;
}
}
} finally {
if(!var18) {
this.addWorkerFailed(var20);
} }
//返回线程的状态
return var18;
} var3 = this.ctl.get();
if(runStateOf(var3) != var4) {
break;
}
}
}
}
以上就是整个线程池的工作原理。
接下来:会把一个线程封装为一个Worker(工作线程),也是一个线程,实现了序列化与队列的功能
Worker在执行完任务后,会循环执获取工作队列中的任务来执行。如下:
final void runWorker(ThreadPoolExecutor.Worker var1) {
Thread var2 = Thread.currentThread();
Runnable var3 = var1.firstTask;
var1.firstTask = null;
var1.unlock();
boolean var4 = true; try {
//循环从队列中去任务执行
while(var3 != null || (var3 = this.getTask()) != null) {
var1.lock();
//判断线程池的状态
if((runStateAtLeast(this.ctl.get(), 536870912) || Thread.interrupted() && runStateAtLeast(this.ctl.get(), 536870912)) && !var2.isInterrupted()) {
var2.interrupt();
} try {
//钩子方法,后续介绍
this.beforeExecute(var2, var3);
Object var5 = null; try {
//^_^终于可以执行任务了。
var3.run();
} catch (RuntimeException var28) {
var5 = var28;
throw var28;
} catch (Error var29) {
var5 = var29;
throw var29;
} catch (Throwable var30) {
var5 = var30;
throw new Error(var30);
} finally {
this.afterExecute(var3, (Throwable)var5);
}
} finally {
var3 = null;
++var1.completedTasks;
var1.unlock();
}
} var4 = false;
} finally {
this.processWorkerExit(var1, var4);
} }
然后来看看getTask()方法
private Runnable getTask() {
boolean var1 = false; while(true) {
int var2 = this.ctl.get();
int var3 = runStateOf(var2);
// 1. rs == SHUTDOWN && workQueue.isEmpty()
// 2. rs >= STOP
if(var3 >= 0 && (var3 >= 536870912 || this.workQueue.isEmpty())) {
//CAS操作,较少线程数
this.decrementWorkerCount();
return null;
} int var4 = workerCountOf(var2);
/核心线程的回收,如果发生超时,则关闭
boolean var5 = this.allowCoreThreadTimeOut || var4 > this.corePoolSize;
if(var4 <= this.maximumPoolSize && (!var5 || !var1) || var4 <= 1 && !this.workQueue.isEmpty()) {
try {
Runnable var6 = var5?(Runnable)this.workQueue.poll(this.keepAliveTime, TimeUnit.NANOSECONDS):(Runnable)this.workQueue.take();
if(var6 != null) {
return var6;
} var1 = true;
} catch (InterruptedException var7) {
var1 = false;
}
} else if(this.compareAndDecrementWorkerCount(var2)) {
return null;
}
}
}
线程池的主要运行流程基本如下,还有很多要学习的,望大家指点。
你所了解的Java线程池的更多相关文章
- Java 线程池框架核心代码分析--转
原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...
- Java线程池使用说明
Java线程池使用说明 转自:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极 ...
- (转载)JAVA线程池管理
平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发攻城师却在这个上面吃了不少苦头.怎么做一套简便的线程开发模式 ...
- Java线程池的那些事
熟悉java多线程的朋友一定十分了解java的线程池,jdk中的核心实现类为java.util.concurrent.ThreadPoolExecutor.大家可能了解到它的原理,甚至看过它的源码:但 ...
- 四种Java线程池用法解析
本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 http://www.jb51.net/article/81843.htm 1.new Thread的弊端 执行一个异步任务你还只是如下 ...
- Java线程池的几种实现 及 常见问题讲解
工作中,经常会涉及到线程.比如有些任务,经常会交与线程去异步执行.抑或服务端程序为每个请求单独建立一个线程处理任务.线程之外的,比如我们用的数据库连接.这些创建销毁或者打开关闭的操作,非常影响系统性能 ...
- Java线程池应用
Executors工具类用于创建Java线程池和定时器. newFixedThreadPool:创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程.在任意点,在大多数 nThread ...
- Java线程池的原理及几类线程池的介绍
刚刚研究了一下线程池,如果有不足之处,请大家不吝赐教,大家共同学习.共同交流. 在什么情况下使用线程池? 单个任务处理的时间比较短 将需处理的任务的数量大 使用线程池的好处: 减少在创建和销毁线程上所 ...
- Java线程池与java.util.concurrent
Java(Android)线程池 介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行 ...
随机推荐
- vs2010安装
1.网盘上2010安装包可用,先下载到电脑上,然后找到setup文件,安装即可 2.下载插件,基本的文本对齐,tab键补齐等功能 3.测试hello world程序 4.出现的问题 一个文件夹下有多个 ...
- C#.net连接Sybase的方法
一 .ODBC方式连接 1 安装Sybase客户端,安装ODBC驱动,配置DSN<略> 2 连接代码 string strconn = "DSN=TEST;SRVR=TEST;D ...
- 剑指offer(leetcode 10.) 正则表达式匹配
这题一年前就做过,当时刚开始刷leetcode,提交了几十次过不去,就放那没管了.今天剑指offer又遇到这题,终于做出来了,用的dp. class Solution { public: bool i ...
- CBE引擎概览
摘录于CBE官方文档:https://www.comblockengine.com/docs/1.0/overview/index/ 架构图总览: Switch Fabric:交换机网络,根据网络环境 ...
- 2019-08-05 纪中NOIP模拟B组
T1 [JZOJ1432] 输油管道 题目描述 请你帮忙设计一个从城市M到城市Z的输油管道,现在已经把整个区域划分为R行C列,每个单元格可能是空的也可能是以下7种基本管道之一: 油从城市M流向Z,‘+ ...
- DBContext基础查询
https://www.cnblogs.com/gosky/p/5752001.html 遍历所有实体 //遍历所有学生 DBSet using (var db = new Entities()) { ...
- centos 配置安装golang
golang的官方下载和安装介绍: https://golang.org/doc/install 按照如下步骤安装绿色版golang ,不需要编译,解压就可以用 1)下载tar.gz 安装包 2)直接 ...
- Sliding Window POJ - 2823 单调队列模板题
Sliding Window POJ - 2823 单调队列模板题 题意 给出一个数列 并且给出一个数m 问每个连续的m中的最小\最大值是多少,并输出 思路 使用单调队列来写,拿最小值来举例 要求区间 ...
- SpringBoot获取http请求参数的方法
SpringBoot获取http请求参数的方法 原文:https://www.cnblogs.com/zhanglijun/p/9403483.html 有七种Java后台获取前端传来参数的方法,稍微 ...
- i5+GT730+B85安装OSX10.10.5 (Yosemite Install(14F27).cdr)
1.用windows磁盘管理工具分出10G分区,指定盘符,但不格式化 2.管理员身份打开“硬盘安装助手” 3.选择cdr文件,取消3个选择框,然后开始写入 4.有可能需要重置安装分区的磁盘标识为AF ...