[转载] Java线程池框架源码分析
转载自http://www.linuxidc.com/Linux/2014-11/108791.htm
相关类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很容易理解,因为它要利用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://www.linuxidc.com/Linux/2014-11/108792.htm 。
回过头来看看任务提交方法的源码:
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线程池ThreadPoolExector源码分析
java线程池ThreadPoolExector源码分析 今天研究了下ThreadPoolExector源码,大致上总结了以下几点跟大家分享下: 一.ThreadPoolExector几个主要变量 先 ...
- 深入浅出Java线程池:源码篇
前言 在上一篇文章深入浅出Java线程池:理论篇中,已经介绍了什么是线程池以及基本的使用.(本来写作的思路是使用篇,但经网友建议后,感觉改为理论篇会更加合适).本文则深入线程池的源码,主要是介绍Thr ...
- Java 线程池框架核心代码分析--转
原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...
- Java 线程池框架核心代码分析
前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的.线程池应运而生,成为我们管理线程的利器.Java 通过Executor接口,提供了一种标准的方法将任务的提交过 ...
- Java调度线程池ScheduledThreadPoolExecutor源码分析
最近新接手的项目里大量使用了ScheduledThreadPoolExecutor类去执行一些定时任务,之前一直没有机会研究这个类的源码,这次趁着机会好好研读一下. 该类主要还是基于ThreadPoo ...
- Java线程池使用和源码分析
1.为什么使用线程池 在多线程编程中一项很重要的功能就是执行任务,而执行任务的方式有很多种,为什么一定需要使用线程池呢?下面我们使用Socket编程处理请求的功能,分别对每种执行任务的方式进行分析. ...
- Java核心复习——线程池ThreadPoolExecutor源码分析
一.线程池的介绍 线程池一种性能优化的重要手段.优化点在于创建线程和销毁线程会带来资源和时间上的消耗,而且线程池可以对线程进行管理,则可以减少这种损耗. 使用线程池的好处如下: 降低资源的消耗 提高响 ...
- 线程池ThreadPoolExecutor源码分析
在阿里编程规约中关于线程池强制了两点,如下: [强制]线程资源必须通过线程池提供,不允许在应用中自行显式创建线程.说明:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源 ...
- Python线程池ThreadPoolExecutor源码分析
在学习concurrent库时遇到了一些问题,后来搞清楚了,这里记录一下 先看个例子: import time from concurrent.futures import ThreadPoolExe ...
随机推荐
- 项目常用Javascript分享,包含常用验证和Cookie操作
function IsEmail(str) { var r = /^[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/; if (r.test(str)) ...
- Pyquery API中文版
Pyquery的用法与jQuery相同,可以直接参考jQuery API学习.
- Apache配置腾讯云SSL证书指引
一.安装Apache 1) 使用yum安装Apache # yum install httpd 2) 修改测试页面 # vim /var/www/html/index.heml PS:修改为测试内容, ...
- ajax请求service报405错误 - 【服务器不允许的方法】
产生原因:web服务器找不到service方法处理请求. 检查方向: ① service方法名称写错 ② service方法参数类型与标准不一致 ③ service方法异常,返回值类型和标准不一致 ④ ...
- 使用WinDBG调试查看C#内存转储文件
有时候我们想查看一个正在运行的程序内存中的数据,可以在任务管理器将内存状态保存为转储文件,并使用WinDBG验证,这里我们来试试: 0.安装WinDBG 1.首先写个代码用来测试 一个class pu ...
- WPF MVVM模式的一些理解
/*本文转自 http://www.cnblogs.com/sirkevin/archive/2012/11/28/2793471.html */ 使用WPF+Mvvm开发一年多,期间由于对Mvvm模 ...
- 转:用STL中的vector动态开辟二维数组
用STL中的vector动态开辟二维数组 源代码:#include <iostream>#include <vector>using namespace std;int mai ...
- Kotlin——最详细的控制语句使用
在前面 的章节中讲解了Kotlin语言中的数据类型.变量与常量的定义.不了解请参见前面的内容: Kotlin从无到有系列之数据类型介绍. Kotlin从无到有系列之变量.常量.注释的使用. 下面详细为 ...
- java如何调用接口方式一
java如何调用接口 其实对于java调用接口进行获取对方服务器的数据在开发中特别常见,然而一些常用的基础的知识总是掌握不牢,让人容易忘记,写下来闲的时候看看,比回想总会好一些. 总体而言,一些东西知 ...
- css 文字和子元素水平垂直居中
关于水平垂直居中,这是一个很简单的问题,但是很多时候,往往简单的东西,反而做不出来.这就是基础不扎实的缘故吧,我参照一些资料,总结了水平垂直居中的几种方法如下: 1 .文字水平垂直居中 这个比较简单, ...