http://www.cnblogs.com/skywang12345/p/3509954.html

线程池示例

在分析线程池之前,先看一个简单的线程池示例。

  1. 1 import java.util.concurrent.Executors;
  2. 2 import java.util.concurrent.ExecutorService;
  3. 3
  4. 4 public class ThreadPoolDemo1 {
  5. 5
  6. 6 public static void main(String[] args) {
  7. 7 // 创建一个可重用固定线程数的线程池
  8. 8 ExecutorService pool = Executors.newFixedThreadPool(2);
  9. 9 // 创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口
  10. 10 Thread ta = new MyThread();
  11. 11 Thread tb = new MyThread();
  12. 12 Thread tc = new MyThread();
  13. 13 Thread td = new MyThread();
  14. 14 Thread te = new MyThread();
  15. 15 // 将线程放入池中进行执行
  16. 16 pool.execute(ta);
  17. 17 pool.execute(tb);
  18. 18 pool.execute(tc);
  19. 19 pool.execute(td);
  20. 20 pool.execute(te);
  21. 21 // 关闭线程池
  22. 22 pool.shutdown();
  23. 23 }
  24. 24 }
  25. 25
  26. 26 class MyThread extends Thread {
  27. 27
  28. 28 @Override
  29. 29 public void run() {
  30. 30 System.out.println(Thread.currentThread().getName()+ " is running.");
  31. 31 }
  32. 32 }

运行结果

  1. pool-1-thread-1 is running.
  2. pool-1-thread-2 is running.
  3. pool-1-thread-1 is running.
  4. pool-1-thread-2 is running.
  5. pool-1-thread-1 is running.

示例中,包括了线程池的创建,将任务添加到线程池中,关闭线程池这3个主要的步骤。稍后,我们会从这3个方面来分析ThreadPoolExecutor。

参考代码(基于JDK1.7.0_40)

Executors完整源码

 

ThreadPoolExecutor完整源码

 

线程池源码分析

(一) 创建“线程池”

下面以newFixedThreadPool()介绍线程池的创建过程。

1. newFixedThreadPool()

newFixedThreadPool()在Executors.java中定义,源码如下:

  1. public static ExecutorService newFixedThreadPool(int nThreads) {
  2. return new ThreadPoolExecutor(nThreads, nThreads,
  3. 0L, TimeUnit.MILLISECONDS,
  4. new LinkedBlockingQueue<Runnable>());
  5. }

说明:newFixedThreadPool(int nThreads)的作用是创建一个线程池,线程池的容量是nThreads。
         newFixedThreadPool()在调用ThreadPoolExecutor()时,会传递一个LinkedBlockingQueue()对象,而LinkedBlockingQueue是单向链表实现的阻塞队列。在线程池中,就是通过该阻塞队列来实现"当线程池中任务数量超过允许的任务数量时,部分任务会阻塞等待"。
关于LinkedBlockingQueue的实现细节,读者可以参考"Java多线程系列--“JUC集合”08之 LinkedBlockingQueue"。

2. ThreadPoolExecutor()

ThreadPoolExecutor()在ThreadPoolExecutor.java中定义,源码如下:

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue<Runnable> workQueue) {
  6. this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
  7. Executors.defaultThreadFactory(), defaultHandler);
  8. }

说明:该函数实际上是调用ThreadPoolExecutor的另外一个构造函数。该函数的源码如下:

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue<Runnable> workQueue,
  6. ThreadFactory threadFactory,
  7. RejectedExecutionHandler handler) {
  8. if (corePoolSize < 0 ||
  9. maximumPoolSize <= 0 ||
  10. maximumPoolSize < corePoolSize ||
  11. keepAliveTime < 0)
  12. throw new IllegalArgumentException();
  13. if (workQueue == null || threadFactory == null || handler == null)
  14. throw new NullPointerException();
  15. // 核心池大小
  16. this.corePoolSize = corePoolSize;
  17. // 最大池大小
  18. this.maximumPoolSize = maximumPoolSize;
  19. // 线程池的等待队列
  20. this.workQueue = workQueue;
  21. this.keepAliveTime = unit.toNanos(keepAliveTime);
  22. // 线程工厂对象
  23. this.threadFactory = threadFactory;
  24. // 拒绝策略的句柄
  25. this.handler = handler;
  26. }

说明:在ThreadPoolExecutor()的构造函数中,进行的是初始化工作。
corePoolSize, maximumPoolSize, unit, keepAliveTime和workQueue这些变量的值是已知的,它们都是通过newFixedThreadPool()传递而来。下面看看threadFactory和handler对象。

2.1 ThreadFactory

线程池中的ThreadFactory是一个线程工厂,线程池创建线程都是通过线程工厂对象(threadFactory)来完成的。
上面所说的threadFactory对象,是通过 Executors.defaultThreadFactory()返回的。Executors.java中的defaultThreadFactory()源码如下:

  1. public static ThreadFactory defaultThreadFactory() {
  2. return new DefaultThreadFactory();
  3. }

defaultThreadFactory()返回DefaultThreadFactory对象。Executors.java中的DefaultThreadFactory()源码如下:

  1. static class DefaultThreadFactory implements ThreadFactory {
  2. private static final AtomicInteger poolNumber = new AtomicInteger(1);
  3. private final ThreadGroup group;
  4. private final AtomicInteger threadNumber = new AtomicInteger(1);
  5. private final String namePrefix;
  6.  
  7. DefaultThreadFactory() {
  8. SecurityManager s = System.getSecurityManager();
  9. group = (s != null) ? s.getThreadGroup() :
  10. Thread.currentThread().getThreadGroup();
  11. namePrefix = "pool-" +
  12. poolNumber.getAndIncrement() +
  13. "-thread-";
  14. }
  15.  
  16. // 提供创建线程的API。
  17. public Thread newThread(Runnable r) {
  18. // 线程对应的任务是Runnable对象r
  19. Thread t = new Thread(group, r,
  20. namePrefix + threadNumber.getAndIncrement(),
  21. 0);
  22. // 设为“非守护线程”
  23. if (t.isDaemon())
  24. t.setDaemon(false);
  25. // 将优先级设为“Thread.NORM_PRIORITY”
  26. if (t.getPriority() != Thread.NORM_PRIORITY)
  27. t.setPriority(Thread.NORM_PRIORITY);
  28. return t;
  29. }
  30. }

说明:ThreadFactory的作用就是提供创建线程的功能的线程工厂。
         它是通过newThread()提供创建线程功能的,下面简单说说newThread()。newThread()创建的线程对应的任务是Runnable对象,它创建的线程都是“非守护线程”而且“线程优先级都是Thread.NORM_PRIORITY”。

2.2 RejectedExecutionHandler

handler是ThreadPoolExecutor中拒绝策略的处理句柄。所谓拒绝策略,是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略。
线程池默认会采用的是defaultHandler策略,即AbortPolicy策略。在AbortPolicy策略中,线程池拒绝任务时会抛出异常!
defaultHandler的定义如下:

  1. private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();

AbortPolicy的源码如下:

  1. public static class AbortPolicy implements RejectedExecutionHandler {
  2. public AbortPolicy() { }
  3.  
  4. // 抛出异常
  5. public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
  6. throw new RejectedExecutionException("Task " + r.toString() +
  7. " rejected from " +
  8. e.toString());
  9. }
  10. }

(二) 添加任务到“线程池”

1. execute()

execute()定义在ThreadPoolExecutor.java中,源码如下:

  1. public void execute(Runnable command) {
  2. // 如果任务为null,则抛出异常。
  3. if (command == null)
  4. throw new NullPointerException();
  5. // 获取ctl对应的int值。该int值保存了"线程池中任务的数量"和"线程池状态"信息
  6. int c = ctl.get();
  7. // 当线程池中的任务数量 < "核心池大小"时,即线程池中少于corePoolSize个任务。
  8. // 则通过addWorker(command, true)新建一个线程,并将任务(command)添加到该线程中;然后,启动该线程从而执行任务。
  9. if (workerCountOf(c) < corePoolSize) {
  10. if (addWorker(command, true))
  11. return;
  12. c = ctl.get();
  13. }
  14. // 当线程池中的任务数量 >= "核心池大小"时,
  15. // 而且,"线程池处于允许状态"时,则尝试将任务添加到阻塞队列中。
  16. if (isRunning(c) && workQueue.offer(command)) {
  17. // 再次确认“线程池状态”,若线程池异常终止了,则删除任务;然后通过reject()执行相应的拒绝策略的内容。
  18. int recheck = ctl.get();
  19. if (! isRunning(recheck) && remove(command))
  20. reject(command);
  21. // 否则,如果"线程池中任务数量"为0,则通过addWorker(null, false)尝试新建一个线程,新建线程对应的任务为null。
  22. else if (workerCountOf(recheck) == 0)
  23. addWorker(null, false);
  24. }
  25. // 通过addWorker(command, false)新建一个线程,并将任务(command)添加到该线程中;然后,启动该线程从而执行任务。
  26. // 如果addWorker(command, false)执行失败,则通过reject()执行相应的拒绝策略的内容。
  27. else if (!addWorker(command, false))
  28. reject(command);
  29. }

说明:execute()的作用是将任务添加到线程池中执行。它会分为3种情况进行处理:
        情况1 -- 如果"线程池中任务数量" < "核心池大小"时,即线程池中少于corePoolSize个任务;此时就新建一个线程,并将该任务添加到线程中进行执行。
        情况2 -- 如果"线程池中任务数量" >= "核心池大小",并且"线程池是允许状态";此时,则将任务添加到阻塞队列中阻塞等待。在该情况下,会再次确认"线程池的状态",如果"第2次读到的线程池状态"和"第1次读到的线程池状态"不同,则从阻塞队列中删除该任务。
        情况3 -- 非以上两种情况。在这种情况下,尝试新建一个线程,并将该任务添加到线程中进行执行。如果执行失败,则通过reject()拒绝该任务。

2. addWorker()

addWorker()的源码如下:

  1. private boolean addWorker(Runnable firstTask, boolean core) {
  2. retry:
  3. // 更新"线程池状态和计数"标记,即更新ctl。
  4. for (;;) {
  5. // 获取ctl对应的int值。该int值保存了"线程池中任务的数量"和"线程池状态"信息
  6. int c = ctl.get();
  7. // 获取线程池状态。
  8. int rs = runStateOf(c);
  9.  
  10. // 有效性检查
  11. if (rs >= SHUTDOWN &&
  12. ! (rs == SHUTDOWN &&
  13. firstTask == null &&
  14. ! workQueue.isEmpty()))
  15. return false;
  16.  
  17. for (;;) {
  18. // 获取线程池中任务的数量。
  19. int wc = workerCountOf(c);
  20. // 如果"线程池中任务的数量"超过限制,则返回false。
  21. if (wc >= CAPACITY ||
  22. wc >= (core ? corePoolSize : maximumPoolSize))
  23. return false;
  24. // 通过CAS函数将c的值+1。操作失败的话,则退出循环。
  25. if (compareAndIncrementWorkerCount(c))
  26. break retry;
  27. c = ctl.get(); // Re-read ctl
  28. // 检查"线程池状态",如果与之前的状态不同,则从retry重新开始。
  29. if (runStateOf(c) != rs)
  30. continue retry;
  31. // else CAS failed due to workerCount change; retry inner loop
  32. }
  33. }
  34.  
  35. boolean workerStarted = false;
  36. boolean workerAdded = false;
  37. Worker w = null;
  38. // 添加任务到线程池,并启动任务所在的线程。
  39. try {
  40. final ReentrantLock mainLock = this.mainLock;
  41. // 新建Worker,并且指定firstTask为Worker的第一个任务。
  42. w = new Worker(firstTask);
  43. // 获取Worker对应的线程。
  44. final Thread t = w.thread;
  45. if (t != null) {
  46. // 获取锁
  47. mainLock.lock();
  48. try {
  49. int c = ctl.get();
  50. int rs = runStateOf(c);
  51.  
  52. // 再次确认"线程池状态"
  53. if (rs < SHUTDOWN ||
  54. (rs == SHUTDOWN && firstTask == null)) {
  55. if (t.isAlive()) // precheck that t is startable
  56. throw new IllegalThreadStateException();
  57. // 将Worker对象(w)添加到"线程池的Worker集合(workers)"中
  58. workers.add(w);
  59. // 更新largestPoolSize
  60. int s = workers.size();
  61. if (s > largestPoolSize)
  62. largestPoolSize = s;
  63. workerAdded = true;
  64. }
  65. } finally {
  66. // 释放锁
  67. mainLock.unlock();
  68. }
  69. // 如果"成功将任务添加到线程池"中,则启动任务所在的线程。
  70. if (workerAdded) {
  71. t.start();
  72. workerStarted = true;
  73. }
  74. }
  75. } finally {
  76. if (! workerStarted)
  77. addWorkerFailed(w);
  78. }
  79. // 返回任务是否启动。
  80. return workerStarted;
  81. }

说明
    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()实现的,源码如下:

  1. public Future<?> submit(Runnable task) {
  2. if (task == null) throw new NullPointerException();
  3. RunnableFuture<Void> ftask = newTaskFor(task, null);
  4. execute(ftask);
  5. return ftask;
  6. }

(三) 关闭“线程池”

shutdown()的源码如下:

  1. public void shutdown() {
  2. final ReentrantLock mainLock = this.mainLock;
  3. // 获取锁
  4. mainLock.lock();
  5. try {
  6. // 检查终止线程池的“线程”是否有权限。
  7. checkShutdownAccess();
  8. // 设置线程池的状态为关闭状态。
  9. advanceRunState(SHUTDOWN);
  10. // 中断线程池中空闲的线程。
  11. interruptIdleWorkers();
  12. // 钩子函数,在ThreadPoolExecutor中没有任何动作。
  13. onShutdown(); // hook for ScheduledThreadPoolExecutor
  14. } finally {
  15. // 释放锁
  16. mainLock.unlock();
  17. }
  18. // 尝试终止线程池
  19. tryTerminate();
  20. }

说明:shutdown()的作用是关闭线程池。


更多内容

1. Java多线程系列目录(共xx篇)

2. Java多线程系列--“JUC线程池”01之 线程池架构

3. Java多线程系列--“JUC线程池”02之 线程池原理(一)

java多线程----线程池源码分析的更多相关文章

  1. java多线程——线程池源码分析(一)

    本文首发于cdream的个人博客,点击获得更好的阅读体验! 欢迎转载,转载请注明出处. 通常应用多线程技术时,我们并不会直接创建一个线程,因为系统启动一个新线程的成本是比较高的,涉及与操作系统的交互, ...

  2. java线程池源码分析

    我们在关闭线程池的时候会使用shutdown()和shutdownNow(),那么问题来了: 这两个方法又什么区别呢? 他们背后的原理是什么呢? 线程池中线程超过了coresize后会怎么操作呢? 为 ...

  3. Java并发编程中线程池源码分析及使用

    当Java处理高并发的时候,线程数量特别的多的时候,而且每个线程都是执行很短的时间就结束了,频繁创建线程和销毁线程需要占用很多系统的资源和时间,会降低系统的工作效率. 参考http://www.cnb ...

  4. 线程池之ThreadPoolExecutor线程池源码分析笔记

    1.线程池的作用 一方面当执行大量异步任务时候线程池能够提供较好的性能,在不使用线程池的时候,每当需要执行异步任务时候是直接 new 一线程进行运行,而线程的创建和销毁是需要开销的.使用线程池时候,线 ...

  5. 【图灵学院10】高并发之java线程池源码分析

    1. 提纲 1)线程池的模块结构 2)示例&原理解析 2. 问题 1)线程池包含哪些东西 2)线程池的运作原理 3)调度线程池的运作原理 4)线程池怎么实现FixRate,FixDelay,他 ...

  6. 线程池之ScheduledThreadPoolExecutor线程池源码分析笔记

    1.ScheduledThreadPoolExecutor 整体结构剖析. 1.1类图介绍 根据上面类图图可以看到Executor其实是一个工具类,里面提供了好多静态方法,根据用户选择返回不同的线程池 ...

  7. 死磕 java集合之DelayQueue源码分析

    问题 (1)DelayQueue是阻塞队列吗? (2)DelayQueue的实现方式? (3)DelayQueue主要用于什么场景? 简介 DelayQueue是java并发包下的延时阻塞队列,常用于 ...

  8. 死磕 java集合之ConcurrentHashMap源码分析(三)

    本章接着上两章,链接直达: 死磕 java集合之ConcurrentHashMap源码分析(一) 死磕 java集合之ConcurrentHashMap源码分析(二) 删除元素 删除元素跟添加元素一样 ...

  9. Java进阶——— 线程池的原理分析

    前言 在了解线程池之前,其实首先出现的疑问是:为什么要使用线程池,其次是了解什么是线程池,最后是如何使用线程池,带着疑问去学习. 为什么要使用 前面多线程文章中,需要使用线程就开启一个新线程,简单方便 ...

随机推荐

  1. Scala日期处理

    计算时间间隔  val d = new java.text.SimpleDateFormat("yyyyMMdd HH:mm:ss").format(new java.util.D ...

  2. hdu6390GuGuFishtion【数论】

    GuGuFishtion Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Tot ...

  3. Solr学习笔记之4、Solr配置文件简介

    Solr学习笔记之4.Solr配置文件简介 摘自<Solr in Action>. 1. solr.xml – Defines one or more cores per Solr ser ...

  4. HDU 5727 - Necklace - [全排列+二分图匹配][Hopcroft-Karp算法模板]

    题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5727 Problem DescriptionSJX has 2*N magic gems. ...

  5. Python的浅拷贝与深拷贝

    定义: =号浅拷贝:在Python中对象的赋值其实就是对象的引用.copy了之后两个仍然是同一个东西.那么他们内部的元素自然也是一样的,对其中一个进行修改,另一个也会跟着变> copy()浅拷贝 ...

  6. Roadblocks--poj3255(次短路)

    题目链接 求次短路的问题: dist[i][0]和dist[i][1]表示从起点1到i的距离和从起点n到i的距离: 次短路要比最短路大但小于其他路: 每条路1--n的距离都可以用dist[i][0] ...

  7. Alignment--POJ1836

    Description In the army, a platoon is composed by n soldiers. During the morning inspection, the sol ...

  8. CentOS VmwareTools安装

    1. 虚拟机菜单栏--虚拟机--安装VMware tools 2. CentOS系统中弹出的VMware tools窗口中--右击VMwaretools.tar.gz--Extract到桌面 3.打开 ...

  9. dedecms批量导出新增文章url和标题

    百度站长工具推出主动提交功能有一段时间了,可以将新产出链接立即通过此方式推送给百度,以保证新链接可以及时被百度收录.那么dedecms如何批量导出新增文章url呢?你可以用标签调用最新文章,可以用sq ...

  10. UIAlertview 添加图片

    - (void)willPresentAlertView:(UIAlertView *)alertView { 在这个方法中, 绘制需要的东西 uiview *myView = [uiview all ...