[转载] 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)创建一个新的线程。
转载于:https://www.cnblogs.com/scott19820130/p/4730763.html
[转载] Java线程池框架源码分析相关推荐
- idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(一)
课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...
- idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(二)
课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...
- JAVA线程池(ThreadPoolExecutor)源码分析
JAVA5提供了多种类型的线程池,如果你对这些线程池的特点以及类型不太熟悉或者非常熟悉,请帮忙看看这篇文章(顺便帮忙解决里面存在的问题,谢谢!): http://xtu-xiaoxin.ite ...
- Java线程池ThreadPoolExecutor源码分析
继承关系 Executor接口 public interface Executor {void execute(Runnable command); } ExecutorService接口 publi ...
- Java并发编程实战(chapter_3)(线程池ThreadPoolExecutor源码分析)
为什么80%的码农都做不了架构师?>>> 这个系列一直没再写,很多原因,中间经历了换工作,熟悉项目,熟悉新团队等等一系列的事情.并发课题对于Java来说是一个又重要又难的一大块 ...
- Java 线程池框架核心代码分析
转载自 Java 线程池框架核心代码分析 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的.线程池应运而生,成为我们管理线程的利器.Java 通过Executo ...
- 从原理到实现丨手把手教你写一个线程池丨源码分析丨线程池内部组成及优化
人人都能学会的线程池 手写完整版 1. 线程池的使用场景 2. 线程池的内部组成 3. 线程池优化 [项目实战]从原理到实现丨手把手教你写一个线程池丨源码分析丨线程池内部组成及优化 内容包括:C/C+ ...
- Java线程池状态判断源码_深入浅出Java线程池:源码篇
前言 在上一篇文章深入浅出Java线程池:理论篇中,已经介绍了什么是线程池以及基本的使用.(本来写作的思路是使用篇,但经网友建议后,感觉改为理论篇会更加合适).本文则深入线程池的源码,主要是介绍Thr ...
- Java 线程池框架核心代码分析--转
原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...
最新文章
- C# SqlBulkCopy 避免插入重复数据(不重复即插入)
- PAT甲级1066 Root of AVL Tree (25分):[C++题解]建立平衡树(AVL树)
- ASP导出数据到excel
- js if for 详解 获取元素方式 及一些js 基础知识
- 最新5G标准必要专利声明量排名:中兴通讯位列全球前三
- spring整合shiro
- Emacs 下安装 python-mode.el
- 如何用python编写操作系统_操作系统介绍、python基础
- 天堂2服务器自动停止,为什么我一进天堂2就与服务器连接中断??/
- 吴翰清​的科技史观:有变革的需求,才有技术的诞生
- FlinkKafkaProducer源码解读
- 10个3d立体字ps金属字图层样式下载
- 第七批中国历史文化名镇名村名单出炉 有你家吗?
- Linux内核网络分层模型——skb核心操作
- 两数互素有什么性质_两个数互素的性质
- 大胆冲数学建模勘误表
- UI5-Fiori初学者导航
- 从零开始学习linux的I2C设备驱动框架——写一个简单的SHT20驱动
- android 分享html代码下载,app在html下载完整可用 包括微信分享
- socket服务端同时监听多个端口号