java线程池框架源代码分析
相关类Executor,Executors。AbstractExecutorService。ExecutorService
Executor:整个线程池运行者框架的顶层接口。
定义了一个execute方法。整个线程运行者框架的核心方法。
public interface Executor { void execute(Runnable command);
}
ExecutorService:这是一个接口它继承自Executor,定义了shutdown。shutdownNow,awaitTermination,submit。invokeAll等方法。
AbstractExecutorService:实现了ExecutorService接口中的submit,invokeAll的方法。
public Future<? > submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
} public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
} public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
在这里。全部submit方法提交的任务终于还是调用了execute方法。execute是接口Executor中定义的方法,AbstractExecutorService没有实现它,
须要子类去实现这种方法。ThreadPoolExecutor继承了AbstractExecutorService,它实现了execute方法。ScheduledThreadPoolExecutor继承自
ThreadPoolExecutor,并覆盖了ThreadPoolExecutor的execute方法。这种方法是线程运行框者架的核心逻辑,不同的线程池运行者有不同的实现逻辑。
AbstractExecutorService的功能较为简单。实现了不同參数的submit。invokeAll方法。
ThreadPoolExecutor线程池运行者:它有一个核心的成员变量:
private final HashSet<Worker> workers = new HashSet<Worker>();
workers能够看做是ThreadPoolExecutor中用于执行任务的线程池。
worker是一个封装了一个Thread对象并实现了Runnable接口的类。
封装Thread非常easy理解,由于它要利用Thread去执行execute方法提交过来的runnable任务。
可是为什么会继承runnable接口呢?
以下是剔除了部分代码的Worker源代码:
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
final Thread thread; Runnable firstTask; Worker(Runnable firstTask) {
setState(-1);
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
} public void run() {
runWorker(this);
} }
Worker是ThreadPoolExecutor的一个内部类,Worker本身实现了Runnable接口,并封装了一个Thread对象,最后在构造方法中获取了一个Runnable对象,这个对象就是ThreadPoolExecutor通过execute提交过来的目标任务。
跟踪runWorker(this)方法:
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock();
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock(); 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();//在这里直接调用了目标任务的run方法,并没有将它传给Thread对象。 } 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);
}
}
回过头来在看看Worker的构造方法:
Worker(Runnable firstTask) {
setState(-1);
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
它将自己传给了自己的成员变量thread。
目标任务被运行的步骤可能就是:Worker的成员变量thread启动后调用worker的run方法。worker的run方法中将自己传给runWorker,runWorker在调用目标运行对象的run方法。
那么thread是何时被运行的呢?
以下看看ThreadPoolExecutor中的一个其它方法:
private boolean addWorker(Runnable firstTask, boolean core) {
......
try {
final ReentrantLock mainLock = this.mainLock;
w = new Worker(firstTask);
final Thread t = w.thread;//这里初始化一个Worker对象w。在将w的成员变量thread付给t
if (t != null) {
mainLock.lock();
try {
int c = ctl.get();
int rs = runStateOf(c); if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();//在这里调用t的start方法。
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
这里为什么会设计的这么绕,我想主要是Worker不仅封装了一个thread,并且对目标任务进行了封装。在执行封装过后的目标任务前,addWorker能够做一些相关操作。
这里只介绍了ThreadPoolExecutor的线程池。那么这个线程池是怎样被维护的。以下介绍几个关键的參数。
private volatile int corePoolSize;
private volatile int maximumPoolSize;
private final BlockingQueue<Runnable> workQueue;
这三个是ThreadPoolExecutor的成员变量,当中workQueue跟县城池没有关系。
workQueue是一个线程安全的堵塞队列。
corePoolSize是线程池的核心大小。maximumPoolSize是线程池的最大大小。
当提交新任务时,假设ThreadPoolExecutor中有线程在执行。而且线程的数量小于corePoolSize,那么就会有新的线程被创建。
假设当前执行的线程数大于corePoolSize,就会放到缓存队列workQueue中。假设缓冲队列也满了。就继续创建线程,直到线程的数量达到maximumPoolSize
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException(); //推断假设当前执行的线程数小于 corePoolSize,加入新的线程(addWorker会加入一个新的线程。上面有介绍),方法直接返回。
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
} //假设当前的执行的线程数大于或等于corePoolSize则新的任务会放到缓存队列中。
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); }
还有些其它的參数:
private volatile ThreadFactory threadFactory //线程的工厂函数。 private volatile RejectedExecutionHandler handler;//任务拒绝的处理类。
private volatile long keepAliveTime;//任务等待的是将。
ThreadPoolExecutor有几个构造方法来初始化这些參数。Executors类将这些參数简化了来获得一个ExecutorService的引用。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
} public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
} public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
} public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
这四个方法中前两个的核心线程数和最大线程数同样,全部可执行的线程数是固定的,<=nThreads。
当任务数大于nThreads时,就是放入缓冲队列中。 后两个方法中,线程数是无边界的,核心线程数是0,最大线程数是整型的最大值,然后假设有线程60秒内没有任务执行的话就销毁。每次有新的任务来,都会创建新的线程或使用曾经创建的线程(60秒内没有任务执行的线程)。
你可能有疑问。既然核心线程数是0,那么全部的任务不是都放到队里里了吗?那么如今就来看看SynchronousQueue这个队里,能够看看这里的介绍http://wsmajunfeng.iteye.com/blog/1629352/。
回过头来看看任务提交方法的源代码:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {//这里是在往队列里方任务,假设不成功就会加入Worker(封装了线程对象)
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);
}
上面链接里的博客提到:offer()往queue里放一个element后马上返回,假设碰巧这个element被还有一个thread取走了,offer方法返回true。觉得offer成功;否则返回false。
试想一下,第一次提交任务的时候,核心线程数为0,此时没有线程所以没有线程从workQueue中取东西,所以这里的workQueue.offer(command)会返回false,那么就会通过addWorker(command, false)创建一个新的线程。
java线程池框架源代码分析的更多相关文章
- Java 线程池框架核心代码分析--转
原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...
- Java 线程池框架核心代码分析
前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的.线程池应运而生,成为我们管理线程的利器.Java 通过Executor接口,提供了一种标准的方法将任务的提交过 ...
- Java线程池原理及分析
线程池是很常用的并发框架,几乎所有需要异步和并发处理任务的程序都可用到线程池. 使用线程池的好处如下: 降低资源消耗:可重复利用已创建的线程池,降低创建和销毁带来的消耗: 提高响应速度:任务到达时,可 ...
- Java线程池使用和分析(一)
线程池是可以控制线程创建.释放,并通过某种策略尝试复用线程去执行任务的一种管理框架,从而实现线程资源与任务之间的一种平衡. 以下分析基于 JDK1.7 以下是本文的目录大纲: 一.线程池架构 二.Th ...
- Java线程池使用和分析(二) - execute()原理
相关文章目录: Java线程池使用和分析(一) Java线程池使用和分析(二) - execute()原理 execute()是 java.util.concurrent.Executor接口中唯一的 ...
- Java 线程池(ThreadPoolExecutor)原理分析与使用
在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 使用线程池的好处 1.降低资源消耗 可以重复利用 ...
- Java线程池(ThreadPoolExecutor)原理分析与使用
在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 使用线程池的好处 1.降低资源消耗 可以重复利用 ...
- 【图灵学院10】高并发之java线程池源码分析
1. 提纲 1)线程池的模块结构 2)示例&原理解析 2. 问题 1)线程池包含哪些东西 2)线程池的运作原理 3)调度线程池的运作原理 4)线程池怎么实现FixRate,FixDelay,他 ...
- Java 线程池(ThreadPoolExecutor)原理分析与实际运用
在我们的开发中"池"的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 有关java线程技术文章还可以推荐阅读:< ...
随机推荐
- 洛谷P1876 开灯
题目背景 该题的题目是不是感到很眼熟呢? 事实上,如果你懂的方法,该题的代码简直不能再短. 但是如果你不懂得呢?那...(自己去想) 题目描述 首先所有的灯都是关的(注意是关!),编号为1的人走过来, ...
- 无法显式调用运算符或访问器 错误处理方法 DLL study
无法显式调用运算符或访问器 错误处理方法 转 无法显式调用运算符或访问器 错误处理方法 反汇编一个dll类库,导出的项目会报出很多bug,其中主要的就是“无法显式调用运算符或访问器”这个错误,看了一下 ...
- 【Codeforces Round #446 (Div. 2) A】Greed
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 贪心选容量大的瓶子就好 [代码] #include <bits/stdc++.h> #define int long l ...
- RMAN异机复制数据库(不同路径)
1.恢复参数文件 设置环境变量: export ORACLE_SID=hncdfhq 登录RMAN: rman target / 在RMAN里把数据库起到nomount状态: startup nomo ...
- SQL Server2008 Hierarchyid数据类型
以往我们在关系数据库中建立树状结构的时候,通常使用ID+ParentID来实现两条 纪录间的父子关系.但这种方式只能标示其相对位置.解决这类问题在SqlServer2005出现之前通常是采用游标来操作 ...
- pragma pack,字节对齐
关于字节对齐 pragma pack 一. 测试代码: // packTest.cpp : Defines the entry point for the console application. / ...
- 翻翻git之---闪烁动画的TextView RevealTextView
转载请注明出处:王亟亟的大牛之路 今天没有P1啦!. 对换工作有想法的.能够找昨天的P1.哈哈 地址:http://blog.csdn.net/ddwhan0123/article/details/5 ...
- Eclipse高效开发插件汇总
以下是我整理的自己开发过程中的常用Eclipse插件,按字母排序: (1) AmaterasUML 介绍:Eclipse的UML插件,支持UML活动图,class图,sequen ...
- [RxJS] RefCount: automatically starting and stopping an execution
With the connect() method on a ConnectableObservable, the programmer is responsible for avoiding lea ...
- php实现记忆化递归--以斐波那契数列为例(还是以边学边做为主,注重练习)
php实现记忆化递归--以斐波那契数列为例(还是以边学边做为主,注重练习) 一.总结 1.递归不优化的话,30层开外就有点吃力了 2.php因为定义变量的时候不用定义变量类型,所以数组里面的类型也是p ...