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. bzoj1676[Usaco2005 Feb]Feed Accounting 饲料计算

    Description Farmer John is trying to figure out when his last shipment of feed arrived. Starting wit ...

  2. allVncClients

    VNC Viewer Free Edition 37  RealVNC Ltd.  15,367  Freeware  1021.58 KB VNC is client and server remo ...

  3. [转载]date命令时间转换

    Linux时间戳和标准时间的互转 在LINUX系统中,有许多场合都使用时间戳的方式表示时间,即从1970年1月1日起至当前的天数或秒数.如/etc/shadow里的密码更改日期和失效日期,还有代理服务 ...

  4. 【转】iOS 解决ipv6问题

    解决ipv6的方法有很多种,由于现在国内的网络运营商还在使用ipv4的网络环境,所以appstore应用不可能大范围去修改自己的服务器, 而且国内的云服务器几乎没有ipv6地址. 这里附上苹果开发平台 ...

  5. html5 app图片预加载

    function Laimgload(){} //图片预加载JS Laimgload.prototype.winHeight = function(){ //计算页面高度 var winHeight ...

  6. [CSAPP笔记][第十章 系统级I/O]

    第十章 系统级I/O 输入/输出(I/O) : 是指主存和外部设备(如磁盘,终端,网络)之间拷贝数据过程. 高级别I/O函数 scanf和printf <<和>> 使用系统级I ...

  7. 只包含schema的dll生成和引用方法

    工作中,所有的tools里有一个project是只包含若干个schema的工程,研究了一下,发现创建这种只包含schema的dll其实非常简单. 首先,在visual studio-new proje ...

  8. ASP.NET 根据现有动态页面生成静态Html

    现有动态页面的格式都是类似 pageName.aspx?ID=1的格式,后面由于发布服务器的原因,要求将动态页面转为静态html后上传. 首先根据页面生成的格式,枚举获取页面html: foreach ...

  9. (转)union和union all的区别

    Union因为要进行重复值扫描,所以效率低.如果合并没有刻意要删除重复行,那么就使用Union All 两个要联合的SQL语句 字段个数必须一样,而且字段类型要“相容”(一致): 如果我们需要将两个s ...

  10. 自动化测试CTS命令

    #!/sbin/sh chmod +x /system/bin/input i=0 while [ "$i" != "10" ] do #am instrume ...