ThreadPoolExecutor是一个非常重要的类,用来构建带有线程池的任务执行器,通过配置不同的参数来构造具有不同规格线程池的任务执行器

写在前面的是:

线程池任务执行器,线程池的定义比较直接,可以看做多个线程的集合。而任务执行器的概念比较的具有针对性,它用来执行任务,通过对线程池的管理实现多任务的并发,是线程池的载体。

线程和任务的区别,线程不是任务,线程是用来执行任务的。

队列是用来存放任务的,不是用来存放线程的。

主要的几个参数解析:

  • 核心线程数(core pool sizes)和最大线程数(maxmum  pool sizes)

一开始两者的存在很让人摸不着头脑,简单的想法是用一个线程数(pool size)表示线程池的大小不就完了吗,不到规定的线程数就创建新的线程来执行新的任务,到了规定的线程数就等待其他线程处理完成,怎么还出现两个控制线程数的参数?

那这两个参数是什么意思干什么用的?

核心线程数:这个数与上面那个简单想法中的数有一个共同点,就是如果当前线程数达不到核心线程数时,不会使用已有的空闲的线程(如果有的话),来了新任务就会创建新的线程。

如果当前线程数达到核心线程数,而且没有空闲线程,那么来了新任务是否要创建新的线程呢?这取决于两点:

  1. 当前的任务队列是否已满。
  2. 线程池的最大线程数。

通过这个问题可以引出最大线程数的概念

最大线程数 : 最大线程数是和任务队列匹配使用的,确切的说是和有长度限制的任务队列(即有界任务队列)匹配使用的。

补充回答上面的问题,ThreadPoolExecutor的线程池拥有一个任务队列,这个任务队列只有在当前线程数>核心线程数的时候才开始使用,如果该线程池使用的任务队列是有界队列,比如10,那么当该队列被新任务填满时也就是说队列中有10个新任务时ThreadPoolExecutor才会创建一个新的线程来执行队列中的一个任务,如果再发生队列被填满,而且依旧没有空闲线程时ThreadPoolExecutor再次创建新的线程,一旦线程的数量等于最大线程数就不再创建新的线程了,如果此时队列中还有10个任务,那么新来的任务就会被拒绝(reject)。

上述是针对有界队列,如果这个任务执行器的队列是无界队列呢?

由于无界队列不会被填满,所以永远不能达到创建新线程所需要的条件,所以也就不会有新线程被创建,所以最大线程数在这种情况下也就失去了其存在的意义。

  • 线程空闲存活时间(keepAliveTime)

在介绍上面的核心线程数和最大线程数时有提到空闲的线程,所谓空闲的线程就是执行完任务之后闲着的线程。

超过这个时间会使得那么核心线程之外的空闲线程被杀死,如果想把这个时间也作用在核心线程上需要设置allowCoreThreadTimeOut(boolean)为true

这里有必要说一下的是,任务执行器如何实现线程的重复利用,当任务执行器执行execute(task)的时候会创建一个worker,它是一个Runnable类,可以看做task的载体,worker包含一个thread对象,这个thread启动的时候执行worker本身的run方法,这样worker和线程就融为一体。当worker的thread start的时候,就会执行worker的run方法,而worker的run会调用任务执行器的runWorker(worker),并将自身传递过去,意思是任务执行器启动了一个worker,而线程重复利用关键就在runWorker中,在启动了一个worker后,worker会从任务执行器中寻找可以运行的任务,而一开始创建worker使用的task就是它的第一个任务。

下面是jdk1.7的源码

//执行一个任务 task

public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) // 将task装配到一个worker中
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}

 

添加一个worker 

private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c); // Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false; for (;;) {
int wc = workerCountOf(c);
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
}
} boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
final ReentrantLock mainLock = this.mainLock;
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int c = ctl.get();
int rs = runStateOf(c); 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) {//如果worker创建成功,就启动它的对应的thread
t.start(); //worker中的tread启动
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}

运行worker

final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {//这里是关键,使用一个while来寻找任务执行器中(主要还是从任务队列中获取)还未执行的task。
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}

  

 

ThreadPoolExecutor参数解析的更多相关文章

  1. springmvc 请求参数解析细节

    springmvc 的请求流程,相信大家已经很熟悉了,不熟悉的同学可以参考下资料! 有了整体流程的概念,是否对其中的实现细节就很清楚呢?我觉得不一定,比如:单是参数解析这块,就是个大学问呢? 首先,我 ...

  2. 写个C#命令行参数解析的小工具

    最近测试工作做的比较多因此时常要创建一些控制台类型的应用程序.因为程序有不同的参数开关,需要在程序启动的时候通过命令行来给程序传递各种开关和参数.直接操作args有些不方便,所以就写了个解析参数的小工 ...

  3. Python--命令行参数解析Demo

    写没有操作界面的程序时,最讨厌的就是参数解析问题,尤其是很多参数那种,下面是一个小Demo,拿出来与各位分享: # -*- coding:utf8 -*- import os import datet ...

  4. Node基础:url查询参数解析之querystring

    模块概述 在nodejs中,提供了querystring这个模块,用来做url查询参数的解析,使用非常简单. 模块总共有四个方法,绝大部分时,我们只会用到 .parse(). .stringify() ...

  5. Zookeeper + Hadoop2.6 集群HA + spark1.6完整搭建与所有参数解析

    废话就不多说了,直接开始啦~ 安装环境变量: 使用linx下的解压软件,解压找到里面的install 或者 ls 运行这个进行安装 yum install gcc yum install gcc-c+ ...

  6. argparse - 命令行选项与参数解析(转)

    argparse - 命令行选项与参数解析(译)Mar 30, 2013 原文:argparse – Command line option and argument parsing 译者:young ...

  7. 一步一步自定义SpringMVC参数解析器

    随心所欲,自定义参数解析器绑定数据. 题图:from Zoommy 干货 SpringMVC解析器用于解析request请求参数并绑定数据到Controller的入参上. 自定义一个参数解析器需要实现 ...

  8. /proc/sys/ 下内核参数解析

    http://blog.itpub.net/15480802/viewspace-753819/ http://blog.itpub.net/15480802/viewspace-753757/ ht ...

  9. Js把URL中的参数解析为一个对象

    <!DOCTYPE HTML> <html> <head> <meta charset="utf-8" /> <title&g ...

随机推荐

  1. Delphi 把字符串读到流中的操作。

    var FReQuestM := TMemoryStream FReQuestM.Write(PChar(FcVoucherXML)^, Length(FcVoucherXML)); 这样就读到流中了 ...

  2. delphi中通过http控件上载文件的问题(紧急) 整理的CSDN 帖子

    http控件能不能实现post文件?要求效果就像普通的html中通过表单(form中<INPUT TYPE="FILE" NAME="FILE1" SIZ ...

  3. linux系统编程之进程(四):进程退出exit,_exit区别即atexit函数(转载)

    一,进程终止有5种方式: 正常退出: 从main函数返回 调用exit 调用_exit 异常退出: 调用abort 由信号终止 二,exit和_exit区别: 关于_exit(): #include ...

  4. hdu5072(鞍山regional problem C):容斥,同色三角形模型

    现场过的第四多的题..当时没什么想法,回来学了下容斥,又听学长讲了一讲,终于把它过了 题目大意:给定n个数,求全部互质或者全部不互质的三元组的个数 先说一下同色三角形模型 n个点 每两个点连一条边(可 ...

  5. maven-Android项目环境搭建

    参考:http://blog.csdn.net/earbao/article/details/40741051 android maven环境搭建: 1.Maven的版本要求3.1.1 2.设置AND ...

  6. 外观模式之C++实现

    说明:本文仅供学习交流,转载请标明出处.欢迎转载. 在我们学习程序设计时经常会用到模块化设计的思想,这一思想是我们首先把要实现的功能用一个模块表示,当用户想完毕某个人物时依次调用相应的函数. 然而.假 ...

  7. LDAP禁止匿名访问

    LDAP默认是允许用户匿名访问的,如下图:在使用工具连接时,勾选匿名绑定后,不需要输入UserDN和密码就可能连接到LDAP服务器,但是只能进行read及search操作.不能做任何的修改及删除操作. ...

  8. CSS基础知识之float

    前段时间写过一篇CSS基础知识之position,当时对float的理解不太准确,被慕课网多名读者指出(原文已修正,如有误导实在抱歉).现对float进行更深入的学习,在此把学习心得分享给大家. 浮动 ...

  9. SuperSocket学习笔记(二)

    上一篇博客SuperSocket学习笔记(一)说明了怎么快速搭建一个服务器端,这篇文章我想深挖一下SuperSocket 1. 每一个客户端连接到服务器端时,服务器端会将客户端的信息保存到一个Sess ...

  10. 使用Fiddler抓取手机上的数据包

    在IIS中,如果网站已经绑定了域名在使用IP是不能访问的,需要添加一个空的主机名与IP的映射才能访问.如下图: Fiddler抓取手机包 在PC上建一个WIFI热的 勾选Fiddler中Tool-&g ...