在jvm中,线程是一个宝贵的资源,创建与销毁都会抢占宝贵的内存资源,为了有效的重用线程,我们用线程池来管理线程,让创建的线程进行复用。

JDK提供了一套Executor框架,帮助我们管理线程,核心成员如下:

它们都在java.util.concurrent包中,是JDK并发包的核心类,其中,Executor是一个interface,只有一个execute方法;Executors扮演着线程工厂的角色;ThreadPoolExecutor表示一个线程池,用来管理我们的线程。

ThreadPoolExecutor通用的构造函数如下:

  1. public ThreadPoolExecutor(
  2. int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit timeUnit,
  3. BlockingQueue<Runnable> workQueue) {
  4. super(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, workQueue);
  5. }

corePoolSize:线程池基本大小;maximumPoolSize:线程池最大大小;keepAliveTime:线程的存活时间;timeUnit:线程活动保持的时间单位;workQueue:工作队列;

说明:BlockingQueue,可以称之为共享队列,后续有专门的文章来介绍。

线程池工作原理:

1.线程池判断核心线程池中线程是否都在工作,如果不是,则创建一个新的工作线程来执行任务,如果核心线程池中的线程都在执行任务,则进入下一步;

2.线程池会判断工作线程是否已经满了,如果工作队列没有满,则将新提交的任务存储在工作队列中等待执行,如果队列满了,则进入下一步;

3.线程池判断线程池(这里判断的是否到达maximumPoolSize)是否都处于执行状态,如果没有的话,则创建一个新的线程来执行,如果满了,则交给饱和策略来处理这个任务,默 认是抛出异常(AbortPolicy),也可以自定义饱和策略。

现在来看一下exwcute方法:

  1. public void execute(Runnable var1) {
  2. if(var1 == null) {
    //如果没有任务,直接抛出异常
  3. throw new NullPointerException();
  4. } else {
    //ct1表示一个AtomicInteger,是一个原子类,非阻塞的,相比与synchronzied与锁,性能更好,后续文章会详细介绍,这里的含义表示在当前的线程数
  5. int var2 = this.ctl.get();
              // 如果当前线程数少于核心线程数,那么直接添加一个 worker 来执行任务
  1. // 创建一个新的线程,并把当前任务 command 作为这个线程的第一个任务(firstTask)
    if(workerCountOf(var2) < this.corePoolSize) {
    //执行的结果会包装为一个FutureTask返回,后续有文章介绍异步(Future)
  2. if(this.addWorker(var1, true)) {
    //return直接返回,保证线程池的高可用,其他的任务交给addWorker()去执行,线程池去处理下一个任务。
  3. return;
  4. }
  5. var2 = this.ctl.get();
  6. }
  7.  
  8. //执行到这里,要么是因为,达到核心线程数,要么是因为,doWorker()执行失败,没有走return
    //首先会判断线程池的运行状态,然后把该任务放入到工作队列中
  9. if(isRunning(var2) && this.workQueue.offer(var1)) {
  10. int var3 = this.ctl.get();
    //如果线程池不在运行状态,移除该任务,并且执行拒绝策略
  11. if(!isRunning(var3) && this.remove(var1)) {
  12. this.reject(var1);
  13. } else if(workerCountOf(var3) == 0) {
    //否则执行该任务
  14. this.addWorker((Runnable)null, false);
  15. }
    //进入这里表示上面的1,2都失败了,在maximumPoolSize来创建线程执行任务,如果失败,则执行决绝策略。
  16. } else if(!this.addWorker(var1, false)) {
  17. this.reject(var1);
  18. }
  19.  
  20. }
  21. }

然后我们来看一下,上面多次调用的doWorker()方法:

  1. //两个参数,分别表示任务与判断是否进入的分支(true为corePoolSize,false为maximumPoolSize)
    private boolean addWorker(Runnable var1, boolean var2) {
  2. while(true) {
    //上面讲述过
  3. int var3 = this.ctl.get();
  4. int var4 = runStateOf(var3);
    // 如果线程池已关闭,并满足以下条件之一,那么不创建新的 worker:
    //var4>0表示,线程池已经关闭
    //若果任务不存在或者工作队列为空或者线程池正在关闭等,表示不执行该任务
  5. if(var4 >= 0 && (var4 != 0 || var1 != null || this.workQueue.isEmpty())) {
  6. return false;
  7. }
  8.  
  9. while(true) {
  10. int var5 = workerCountOf(var3);
  11. if(var5 >= 536870911 || var5 >= (var2?this.corePoolSize:this.maximumPoolSize)) {
    //var2通过true或者false来判断当前线程池的状况,大于corePoolSize或者maximumPoolSize,则决绝执行。
  12. return false;
  13. }
    //成功则创建线程执行任务
    //compareIncreamentWorkerCount()该方法表示原子操作,底层调用的是AtomicInteger.compareAndSet(int var1,int var2),表示当前值为var1,则设置为var2,后续会详细介绍
    //如果失败,说明不止有一个线程同时操作此方法,创建线程。
  14. if(this.compareAndIncrementWorkerCount(var3)) {
    // worker 是否已经启动
  15. boolean var18 = false;
    / 是否已将这个 worker 添加到 workers 这个 HashSet
  16. boolean var19 = false;
  17. ThreadPoolExecutor.Worker var20 = null;
  18.  
  19. try {
    //创建线程执行任务
  20. var20 = new ThreadPoolExecutor.Worker(var1);
    //Worker的构造方法会调用 ThreadFactory 来创建一个新的线程
  21. Thread var6 = var20.thread;
  22. if(var6 != null) {
    //重入锁,锁住下列操作,保证原子性,保证在操作过程中,线程池不会被关闭
  23. ReentrantLock var7 = this.mainLock;
  24. var7.lock();
  25.  
  26. try {
  27. int var8 = runStateOf(this.ctl.get());
  28. if(var8 < 0 || var8 == 0 && var1 == null) {
  29. if(var6.isAlive()) {
  30. throw new IllegalThreadStateException();
  31. }
    //增加到Set集合中。用于执行
  32. this.workers.add(var20);
  33. int var9 = this.workers.size();
    //因为 workers 是不断增加减少的,通过这个值可以知道线程池的大小曾经达到的最大值
  34. if(var9 > this.largestPoolSize) {
  35. this.largestPoolSize = var9;
  36. }
  37.  
  38. var19 = true;
  39. }
  40. } finally {
  41. var7.unlock();
  42. }
  43.  
  44. if(var19) {
    //启动线程执行
  45. var6.start();
  46. var18 = true;
  47. }
  48. }
  49. } finally {
  50. if(!var18) {
  51. this.addWorkerFailed(var20);
  52. }
  53.  
  54. }
  55. //返回线程的状态
  56. return var18;
  57. }
  58.  
  59. var3 = this.ctl.get();
  60. if(runStateOf(var3) != var4) {
  61. break;
  62. }
  63. }
  64. }
  65. }

以上就是整个线程池的工作原理。

接下来:会把一个线程封装为一个Worker(工作线程),也是一个线程,实现了序列化与队列的功能

Worker在执行完任务后,会循环执获取工作队列中的任务来执行。如下:

  1. final void runWorker(ThreadPoolExecutor.Worker var1) {
  2. Thread var2 = Thread.currentThread();
  3. Runnable var3 = var1.firstTask;
  4. var1.firstTask = null;
  5. var1.unlock();
  6. boolean var4 = true;
  7.  
  8. try {
    //循环从队列中去任务执行
  9. while(var3 != null || (var3 = this.getTask()) != null) {
  10. var1.lock();
    //判断线程池的状态
  11. if((runStateAtLeast(this.ctl.get(), 536870912) || Thread.interrupted() && runStateAtLeast(this.ctl.get(), 536870912)) && !var2.isInterrupted()) {
  12. var2.interrupt();
  13. }
  14.  
  15. try {
    //钩子方法,后续介绍
  16. this.beforeExecute(var2, var3);
  17. Object var5 = null;
  18.  
  19. try {
    //^_^终于可以执行任务了。
  20. var3.run();
  21. } catch (RuntimeException var28) {
  22. var5 = var28;
  23. throw var28;
  24. } catch (Error var29) {
  25. var5 = var29;
  26. throw var29;
  27. } catch (Throwable var30) {
  28. var5 = var30;
  29. throw new Error(var30);
  30. } finally {
  31. this.afterExecute(var3, (Throwable)var5);
  32. }
  33. } finally {
  34. var3 = null;
  35. ++var1.completedTasks;
  36. var1.unlock();
  37. }
  38. }
  39.  
  40. var4 = false;
  41. } finally {
  42. this.processWorkerExit(var1, var4);
  43. }
  44.  
  45. }

然后来看看getTask()方法

  1. private Runnable getTask() {
  2. boolean var1 = false;
  3.  
  4. while(true) {
  5. int var2 = this.ctl.get();
  6. int var3 = runStateOf(var2);
    // 1. rs == SHUTDOWN && workQueue.isEmpty()
    // 2. rs >= STOP
  7. if(var3 >= 0 && (var3 >= 536870912 || this.workQueue.isEmpty())) {
    //CAS操作,较少线程数
  8. this.decrementWorkerCount();
  9. return null;
  10. }
  11.  
  12. int var4 = workerCountOf(var2);
    /核心线程的回收,如果发生超时,则关闭
  13. boolean var5 = this.allowCoreThreadTimeOut || var4 > this.corePoolSize;
  14. if(var4 <= this.maximumPoolSize && (!var5 || !var1) || var4 <= 1 && !this.workQueue.isEmpty()) {
  15. try {
  16. Runnable var6 = var5?(Runnable)this.workQueue.poll(this.keepAliveTime, TimeUnit.NANOSECONDS):(Runnable)this.workQueue.take();
  17. if(var6 != null) {
  18. return var6;
  19. }
  20.  
  21. var1 = true;
  22. } catch (InterruptedException var7) {
  23. var1 = false;
  24. }
  25. } else if(this.compareAndDecrementWorkerCount(var2)) {
  26. return null;
  27. }
  28. }
  29. }

线程池的主要运行流程基本如下,还有很多要学习的,望大家指点。

你所了解的Java线程池的更多相关文章

  1. Java 线程池框架核心代码分析--转

    原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...

  2. Java线程池使用说明

    Java线程池使用说明 转自:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极 ...

  3. (转载)JAVA线程池管理

    平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发攻城师却在这个上面吃了不少苦头.怎么做一套简便的线程开发模式 ...

  4. Java线程池的那些事

    熟悉java多线程的朋友一定十分了解java的线程池,jdk中的核心实现类为java.util.concurrent.ThreadPoolExecutor.大家可能了解到它的原理,甚至看过它的源码:但 ...

  5. 四种Java线程池用法解析

    本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 http://www.jb51.net/article/81843.htm 1.new Thread的弊端 执行一个异步任务你还只是如下 ...

  6. Java线程池的几种实现 及 常见问题讲解

    工作中,经常会涉及到线程.比如有些任务,经常会交与线程去异步执行.抑或服务端程序为每个请求单独建立一个线程处理任务.线程之外的,比如我们用的数据库连接.这些创建销毁或者打开关闭的操作,非常影响系统性能 ...

  7. Java线程池应用

    Executors工具类用于创建Java线程池和定时器. newFixedThreadPool:创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程.在任意点,在大多数 nThread ...

  8. Java线程池的原理及几类线程池的介绍

    刚刚研究了一下线程池,如果有不足之处,请大家不吝赐教,大家共同学习.共同交流. 在什么情况下使用线程池? 单个任务处理的时间比较短 将需处理的任务的数量大 使用线程池的好处: 减少在创建和销毁线程上所 ...

  9. Java线程池与java.util.concurrent

    Java(Android)线程池 介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行 ...

随机推荐

  1. centos python版本升级到3.x

    Linux(CentOS)下将Python的版本升级为3.6.2的方法 1.检查确认系统的相关信息 查看内核版本[root@zstest1 ~]# cat /etc/redhat-release Ce ...

  2. ScrollView示例(转载)

    // 初始化var scrollView = new ccui.ScrollView(); // 设置方向scrollView.setDirection(ccui.ScrollView.DIR_VER ...

  3. PP:Classification of Time-Series Images Using Deep Convolutional Neural Networks

    The 10th international conference on machine vision; C类 Methodology: 非主流方法 2 stages: 1. convert time ...

  4. MS yc

    # word - operate标题栏 菜单栏 工具栏 页面 状态栏 字体阴影 背景色 着重号 项目符号 数字编码 格式刷

  5. SpringCloud大白话之服务注册中心

    SpringCloud-Eureka白话说明 eureka.client.register-with-eureka 属性表示是否将自己注册到Eureka Server, 默认为true. 由于当前应用 ...

  6. bzoj3626: [LNOI2014]LCA (树链剖分)

    很神奇的方法 感觉是有生之年都想不到正解的这种 考虑对i 到根的节点权值 + 1,则从根到z的路径和就是lca(i,z)的深度 所以依次把0 ~ n - 1的点权值 + 1 对于询问[l, r] 这个 ...

  7. 出现 HTTP Status 500 - Servlet.init() for servlet springmvc threw exception 异常

    出现这种异常在网上搜了搜 ,大多数都是说jdk和tomcat版本的问题:而我前几天都是运行得好好的,今天就编写了代码一运行项目发现报了这个错误.后台仔细看了看错误信息.结果是在你的项目中有相同的req ...

  8. Git-配置SSH公钥

    前言:Git是分布式的代码管理工具,远程的代码管理是基于SSH的,所以要使用远程的Git则需要SSH的配置. 以下操作都在git-bash命令行中进行. 查看所有配置项: git config --l ...

  9. python的类定义与实例化

    理解类属性和实例属性: 直接在类里面定义的变量叫类属性,类属性是公有的,每个类实例化就自动拥有类的属性,且实例化对象的这个属性的初始地址指向类属性的地址 如果直接给实例化对象的属性赋值这样会改变该属性 ...

  10. 计算几何-多边形内核判定-HPI-poj3335

    This article is made by Jason-Cow.Welcome to reprint.But please post the article's address. 先解决一个问题, ...