java多线程----线程池源码分析
http://www.cnblogs.com/skywang12345/p/3509954.html
线程池示例
在分析线程池之前,先看一个简单的线程池示例。
- 1 import java.util.concurrent.Executors;
- 2 import java.util.concurrent.ExecutorService;
- 3
- 4 public class ThreadPoolDemo1 {
- 5
- 6 public static void main(String[] args) {
- 7 // 创建一个可重用固定线程数的线程池
- 8 ExecutorService pool = Executors.newFixedThreadPool(2);
- 9 // 创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口
- 10 Thread ta = new MyThread();
- 11 Thread tb = new MyThread();
- 12 Thread tc = new MyThread();
- 13 Thread td = new MyThread();
- 14 Thread te = new MyThread();
- 15 // 将线程放入池中进行执行
- 16 pool.execute(ta);
- 17 pool.execute(tb);
- 18 pool.execute(tc);
- 19 pool.execute(td);
- 20 pool.execute(te);
- 21 // 关闭线程池
- 22 pool.shutdown();
- 23 }
- 24 }
- 25
- 26 class MyThread extends Thread {
- 27
- 28 @Override
- 29 public void run() {
- 30 System.out.println(Thread.currentThread().getName()+ " is running.");
- 31 }
- 32 }
运行结果:
- pool-1-thread-1 is running.
- pool-1-thread-2 is running.
- pool-1-thread-1 is running.
- pool-1-thread-2 is running.
- pool-1-thread-1 is running.
示例中,包括了线程池的创建,将任务添加到线程池中,关闭线程池这3个主要的步骤。稍后,我们会从这3个方面来分析ThreadPoolExecutor。
参考代码(基于JDK1.7.0_40)
Executors完整源码
ThreadPoolExecutor完整源码
线程池源码分析
(一) 创建“线程池”
下面以newFixedThreadPool()介绍线程池的创建过程。
1. newFixedThreadPool()
newFixedThreadPool()在Executors.java中定义,源码如下:
- public static ExecutorService newFixedThreadPool(int nThreads) {
- return new ThreadPoolExecutor(nThreads, nThreads,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<Runnable>());
- }
说明:newFixedThreadPool(int nThreads)的作用是创建一个线程池,线程池的容量是nThreads。
newFixedThreadPool()在调用ThreadPoolExecutor()时,会传递一个LinkedBlockingQueue()对象,而LinkedBlockingQueue是单向链表实现的阻塞队列。在线程池中,就是通过该阻塞队列来实现"当线程池中任务数量超过允许的任务数量时,部分任务会阻塞等待"。
关于LinkedBlockingQueue的实现细节,读者可以参考"Java多线程系列--“JUC集合”08之 LinkedBlockingQueue"。
2. ThreadPoolExecutor()
ThreadPoolExecutor()在ThreadPoolExecutor.java中定义,源码如下:
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue) {
- this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
- Executors.defaultThreadFactory(), defaultHandler);
- }
说明:该函数实际上是调用ThreadPoolExecutor的另外一个构造函数。该函数的源码如下:
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue,
- ThreadFactory threadFactory,
- RejectedExecutionHandler handler) {
- if (corePoolSize < 0 ||
- maximumPoolSize <= 0 ||
- maximumPoolSize < corePoolSize ||
- keepAliveTime < 0)
- throw new IllegalArgumentException();
- if (workQueue == null || threadFactory == null || handler == null)
- throw new NullPointerException();
- // 核心池大小
- this.corePoolSize = corePoolSize;
- // 最大池大小
- this.maximumPoolSize = maximumPoolSize;
- // 线程池的等待队列
- this.workQueue = workQueue;
- this.keepAliveTime = unit.toNanos(keepAliveTime);
- // 线程工厂对象
- this.threadFactory = threadFactory;
- // 拒绝策略的句柄
- this.handler = handler;
- }
说明:在ThreadPoolExecutor()的构造函数中,进行的是初始化工作。
corePoolSize, maximumPoolSize, unit, keepAliveTime和workQueue这些变量的值是已知的,它们都是通过newFixedThreadPool()传递而来。下面看看threadFactory和handler对象。
2.1 ThreadFactory
线程池中的ThreadFactory是一个线程工厂,线程池创建线程都是通过线程工厂对象(threadFactory)来完成的。
上面所说的threadFactory对象,是通过 Executors.defaultThreadFactory()返回的。Executors.java中的defaultThreadFactory()源码如下:
- public static ThreadFactory defaultThreadFactory() {
- return new DefaultThreadFactory();
- }
defaultThreadFactory()返回DefaultThreadFactory对象。Executors.java中的DefaultThreadFactory()源码如下:
- static class DefaultThreadFactory implements ThreadFactory {
- private static final AtomicInteger poolNumber = new AtomicInteger(1);
- private final ThreadGroup group;
- private final AtomicInteger threadNumber = new AtomicInteger(1);
- private final String namePrefix;
- DefaultThreadFactory() {
- SecurityManager s = System.getSecurityManager();
- group = (s != null) ? s.getThreadGroup() :
- Thread.currentThread().getThreadGroup();
- namePrefix = "pool-" +
- poolNumber.getAndIncrement() +
- "-thread-";
- }
- // 提供创建线程的API。
- public Thread newThread(Runnable r) {
- // 线程对应的任务是Runnable对象r
- Thread t = new Thread(group, r,
- namePrefix + threadNumber.getAndIncrement(),
- 0);
- // 设为“非守护线程”
- if (t.isDaemon())
- t.setDaemon(false);
- // 将优先级设为“Thread.NORM_PRIORITY”
- if (t.getPriority() != Thread.NORM_PRIORITY)
- t.setPriority(Thread.NORM_PRIORITY);
- return t;
- }
- }
说明:ThreadFactory的作用就是提供创建线程的功能的线程工厂。
它是通过newThread()提供创建线程功能的,下面简单说说newThread()。newThread()创建的线程对应的任务是Runnable对象,它创建的线程都是“非守护线程”而且“线程优先级都是Thread.NORM_PRIORITY”。
2.2 RejectedExecutionHandler
handler是ThreadPoolExecutor中拒绝策略的处理句柄。所谓拒绝策略,是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略。
线程池默认会采用的是defaultHandler策略,即AbortPolicy策略。在AbortPolicy策略中,线程池拒绝任务时会抛出异常!
defaultHandler的定义如下:
- private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
AbortPolicy的源码如下:
- public static class AbortPolicy implements RejectedExecutionHandler {
- public AbortPolicy() { }
- // 抛出异常
- public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
- throw new RejectedExecutionException("Task " + r.toString() +
- " rejected from " +
- e.toString());
- }
- }
(二) 添加任务到“线程池”
1. execute()
execute()定义在ThreadPoolExecutor.java中,源码如下:
- public void execute(Runnable command) {
- // 如果任务为null,则抛出异常。
- if (command == null)
- throw new NullPointerException();
- // 获取ctl对应的int值。该int值保存了"线程池中任务的数量"和"线程池状态"信息
- int c = ctl.get();
- // 当线程池中的任务数量 < "核心池大小"时,即线程池中少于corePoolSize个任务。
- // 则通过addWorker(command, true)新建一个线程,并将任务(command)添加到该线程中;然后,启动该线程从而执行任务。
- if (workerCountOf(c) < corePoolSize) {
- if (addWorker(command, true))
- return;
- c = ctl.get();
- }
- // 当线程池中的任务数量 >= "核心池大小"时,
- // 而且,"线程池处于允许状态"时,则尝试将任务添加到阻塞队列中。
- if (isRunning(c) && workQueue.offer(command)) {
- // 再次确认“线程池状态”,若线程池异常终止了,则删除任务;然后通过reject()执行相应的拒绝策略的内容。
- int recheck = ctl.get();
- if (! isRunning(recheck) && remove(command))
- reject(command);
- // 否则,如果"线程池中任务数量"为0,则通过addWorker(null, false)尝试新建一个线程,新建线程对应的任务为null。
- else if (workerCountOf(recheck) == 0)
- addWorker(null, false);
- }
- // 通过addWorker(command, false)新建一个线程,并将任务(command)添加到该线程中;然后,启动该线程从而执行任务。
- // 如果addWorker(command, false)执行失败,则通过reject()执行相应的拒绝策略的内容。
- else if (!addWorker(command, false))
- reject(command);
- }
说明:execute()的作用是将任务添加到线程池中执行。它会分为3种情况进行处理:
情况1 -- 如果"线程池中任务数量" < "核心池大小"时,即线程池中少于corePoolSize个任务;此时就新建一个线程,并将该任务添加到线程中进行执行。
情况2 -- 如果"线程池中任务数量" >= "核心池大小",并且"线程池是允许状态";此时,则将任务添加到阻塞队列中阻塞等待。在该情况下,会再次确认"线程池的状态",如果"第2次读到的线程池状态"和"第1次读到的线程池状态"不同,则从阻塞队列中删除该任务。
情况3 -- 非以上两种情况。在这种情况下,尝试新建一个线程,并将该任务添加到线程中进行执行。如果执行失败,则通过reject()拒绝该任务。
2. addWorker()
addWorker()的源码如下:
- private boolean addWorker(Runnable firstTask, boolean core) {
- retry:
- // 更新"线程池状态和计数"标记,即更新ctl。
- for (;;) {
- // 获取ctl对应的int值。该int值保存了"线程池中任务的数量"和"线程池状态"信息
- int c = ctl.get();
- // 获取线程池状态。
- int rs = runStateOf(c);
- // 有效性检查
- if (rs >= SHUTDOWN &&
- ! (rs == SHUTDOWN &&
- firstTask == null &&
- ! workQueue.isEmpty()))
- return false;
- for (;;) {
- // 获取线程池中任务的数量。
- int wc = workerCountOf(c);
- // 如果"线程池中任务的数量"超过限制,则返回false。
- if (wc >= CAPACITY ||
- wc >= (core ? corePoolSize : maximumPoolSize))
- return false;
- // 通过CAS函数将c的值+1。操作失败的话,则退出循环。
- if (compareAndIncrementWorkerCount(c))
- break retry;
- c = ctl.get(); // Re-read ctl
- // 检查"线程池状态",如果与之前的状态不同,则从retry重新开始。
- 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;
- // 新建Worker,并且指定firstTask为Worker的第一个任务。
- w = new Worker(firstTask);
- // 获取Worker对应的线程。
- final Thread t = w.thread;
- if (t != null) {
- // 获取锁
- mainLock.lock();
- try {
- 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();
- // 将Worker对象(w)添加到"线程池的Worker集合(workers)"中
- workers.add(w);
- // 更新largestPoolSize
- int s = workers.size();
- if (s > largestPoolSize)
- largestPoolSize = s;
- workerAdded = true;
- }
- } finally {
- // 释放锁
- mainLock.unlock();
- }
- // 如果"成功将任务添加到线程池"中,则启动任务所在的线程。
- if (workerAdded) {
- t.start();
- workerStarted = true;
- }
- }
- } finally {
- if (! workerStarted)
- addWorkerFailed(w);
- }
- // 返回任务是否启动。
- return workerStarted;
- }
说明:
addWorker(Runnable firstTask, boolean core) 的作用是将任务(firstTask)添加到线程池中,并启动该任务。
core为true的话,则以corePoolSize为界限,若"线程池中已有任务数量>=corePoolSize",则返回false;core为false的话,则以maximumPoolSize为界限,若"线程池中已有任务数量>=maximumPoolSize",则返回false。
addWorker()会先通过for循环不断尝试更新ctl状态,ctl记录了"线程池中任务数量和线程池状态"。
更新成功之后,再通过try模块来将任务添加到线程池中,并启动任务所在的线程。
从addWorker()中,我们能清晰的发现:线程池在添加任务时,会创建任务对应的Worker对象;而一个Workder对象包含一个Thread对象。(01) 通过将Worker对象添加到"线程的workers集合"中,从而实现将任务添加到线程池中。 (02) 通过启动Worker对应的Thread线程,则执行该任务。
3. submit()
补充说明一点,submit()实际上也是通过调用execute()实现的,源码如下:
- public Future<?> submit(Runnable task) {
- if (task == null) throw new NullPointerException();
- RunnableFuture<Void> ftask = newTaskFor(task, null);
- execute(ftask);
- return ftask;
- }
(三) 关闭“线程池”
shutdown()的源码如下:
- public void shutdown() {
- final ReentrantLock mainLock = this.mainLock;
- // 获取锁
- mainLock.lock();
- try {
- // 检查终止线程池的“线程”是否有权限。
- checkShutdownAccess();
- // 设置线程池的状态为关闭状态。
- advanceRunState(SHUTDOWN);
- // 中断线程池中空闲的线程。
- interruptIdleWorkers();
- // 钩子函数,在ThreadPoolExecutor中没有任何动作。
- onShutdown(); // hook for ScheduledThreadPoolExecutor
- } finally {
- // 释放锁
- mainLock.unlock();
- }
- // 尝试终止线程池
- tryTerminate();
- }
说明:shutdown()的作用是关闭线程池。
更多内容
2. Java多线程系列--“JUC线程池”01之 线程池架构
3. Java多线程系列--“JUC线程池”02之 线程池原理(一)
java多线程----线程池源码分析的更多相关文章
- java多线程——线程池源码分析(一)
本文首发于cdream的个人博客,点击获得更好的阅读体验! 欢迎转载,转载请注明出处. 通常应用多线程技术时,我们并不会直接创建一个线程,因为系统启动一个新线程的成本是比较高的,涉及与操作系统的交互, ...
- java线程池源码分析
我们在关闭线程池的时候会使用shutdown()和shutdownNow(),那么问题来了: 这两个方法又什么区别呢? 他们背后的原理是什么呢? 线程池中线程超过了coresize后会怎么操作呢? 为 ...
- Java并发编程中线程池源码分析及使用
当Java处理高并发的时候,线程数量特别的多的时候,而且每个线程都是执行很短的时间就结束了,频繁创建线程和销毁线程需要占用很多系统的资源和时间,会降低系统的工作效率. 参考http://www.cnb ...
- 线程池之ThreadPoolExecutor线程池源码分析笔记
1.线程池的作用 一方面当执行大量异步任务时候线程池能够提供较好的性能,在不使用线程池的时候,每当需要执行异步任务时候是直接 new 一线程进行运行,而线程的创建和销毁是需要开销的.使用线程池时候,线 ...
- 【图灵学院10】高并发之java线程池源码分析
1. 提纲 1)线程池的模块结构 2)示例&原理解析 2. 问题 1)线程池包含哪些东西 2)线程池的运作原理 3)调度线程池的运作原理 4)线程池怎么实现FixRate,FixDelay,他 ...
- 线程池之ScheduledThreadPoolExecutor线程池源码分析笔记
1.ScheduledThreadPoolExecutor 整体结构剖析. 1.1类图介绍 根据上面类图图可以看到Executor其实是一个工具类,里面提供了好多静态方法,根据用户选择返回不同的线程池 ...
- 死磕 java集合之DelayQueue源码分析
问题 (1)DelayQueue是阻塞队列吗? (2)DelayQueue的实现方式? (3)DelayQueue主要用于什么场景? 简介 DelayQueue是java并发包下的延时阻塞队列,常用于 ...
- 死磕 java集合之ConcurrentHashMap源码分析(三)
本章接着上两章,链接直达: 死磕 java集合之ConcurrentHashMap源码分析(一) 死磕 java集合之ConcurrentHashMap源码分析(二) 删除元素 删除元素跟添加元素一样 ...
- Java进阶——— 线程池的原理分析
前言 在了解线程池之前,其实首先出现的疑问是:为什么要使用线程池,其次是了解什么是线程池,最后是如何使用线程池,带着疑问去学习. 为什么要使用 前面多线程文章中,需要使用线程就开启一个新线程,简单方便 ...
随机推荐
- Scala日期处理
计算时间间隔 val d = new java.text.SimpleDateFormat("yyyyMMdd HH:mm:ss").format(new java.util.D ...
- hdu6390GuGuFishtion【数论】
GuGuFishtion Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Tot ...
- Solr学习笔记之4、Solr配置文件简介
Solr学习笔记之4.Solr配置文件简介 摘自<Solr in Action>. 1. solr.xml – Defines one or more cores per Solr ser ...
- HDU 5727 - Necklace - [全排列+二分图匹配][Hopcroft-Karp算法模板]
题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5727 Problem DescriptionSJX has 2*N magic gems. ...
- Python的浅拷贝与深拷贝
定义: =号浅拷贝:在Python中对象的赋值其实就是对象的引用.copy了之后两个仍然是同一个东西.那么他们内部的元素自然也是一样的,对其中一个进行修改,另一个也会跟着变> copy()浅拷贝 ...
- Roadblocks--poj3255(次短路)
题目链接 求次短路的问题: dist[i][0]和dist[i][1]表示从起点1到i的距离和从起点n到i的距离: 次短路要比最短路大但小于其他路: 每条路1--n的距离都可以用dist[i][0] ...
- Alignment--POJ1836
Description In the army, a platoon is composed by n soldiers. During the morning inspection, the sol ...
- CentOS VmwareTools安装
1. 虚拟机菜单栏--虚拟机--安装VMware tools 2. CentOS系统中弹出的VMware tools窗口中--右击VMwaretools.tar.gz--Extract到桌面 3.打开 ...
- dedecms批量导出新增文章url和标题
百度站长工具推出主动提交功能有一段时间了,可以将新产出链接立即通过此方式推送给百度,以保证新链接可以及时被百度收录.那么dedecms如何批量导出新增文章url呢?你可以用标签调用最新文章,可以用sq ...
- UIAlertview 添加图片
- (void)willPresentAlertView:(UIAlertView *)alertView { 在这个方法中, 绘制需要的东西 uiview *myView = [uiview all ...