一、简介 
1)线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, 
long keepAliveTime, TimeUnit unit, 
BlockingQueue<Runnable> workQueue, 
RejectedExecutionHandler handler)

参数讲解:
corePoolSize: 线程池维护线程的最少数量 
maximumPoolSize:线程池维护线程的最大数量 
keepAliveTime: 线程池维护线程所允许的空闲时间 
unit: 线程池维护线程所允许的空闲时间的单位 
workQueue: 线程池所使用的缓冲队列 
handler: 线程池对拒绝任务的处理策略 
unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性: 
NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。 
workQueue我常用的是:java.util.concurrent.ArrayBlockingQueue 
handler有四个选择: 
ThreadPoolExecutor.AbortPolicy() 
直接抛出java.util.concurrent.RejectedExecutionException异常 
ThreadPoolExecutor.CallerRunsPolicy() 
重试添加当前的任务,他会自动重复调用execute()方法,交由调用者线程来执行此Runnable任务 
ThreadPoolExecutor.DiscardOldestPolicy() 
抛弃旧的任务 
ThreadPoolExecutor.DiscardPolicy() 
抛弃当前的任务

2)一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。

当一个任务通过execute(Runnable)方法欲添加到线程池时 :

如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。 
如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。 
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。 
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。

也就是:处理任务的优先级为: 
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。先填满corepoolSize,然后在填满缓存队列,然后填满maximumplloSize,最后处理拒绝任务。

当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

二、多线程例子

  1. package demo;
  2. import java.io.Serializable;
  3. import java.util.concurrent.ArrayBlockingQueue;
  4. import java.util.concurrent.ThreadPoolExecutor;
  5. import java.util.concurrent.TimeUnit;
  6. public class TestThreadPool2
  7. {
  8. private static int produceTaskSleepTime = 2;
  9. private static int produceTaskMaxNumber = 10;
  10. public static void main(String[] args)
  11. {
  12. // 构造一个线程池
  13. ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),
  14. new ThreadPoolExecutor.DiscardOldestPolicy());
  15. for (int i = 1; i <= produceTaskMaxNumber; i++)
  16. {
  17. try
  18. {
  19. // 产生一个任务,并将其加入到线程池
  20. String task = "task@ " + i;
  21. System.out.println("put " + task);
  22. threadPool.execute(new ThreadPoolTask(task));
  23. // 便于观察,等待一段时间
  24. Thread.sleep(produceTaskSleepTime);
  25. }
  26. catch (Exception e)
  27. {
  28. e.printStackTrace();
  29. }
  30. }
  31. }
  32. }
  33. /**
  34. * 线程池执行的任务
  35. */
  36. class ThreadPoolTask implements Runnable, Serializable
  37. {
  38. private static final long serialVersionUID = 0;
  39. private static int consumeTaskSleepTime = 2000;
  40. // 保存任务所需要的数据
  41. private Object threadPoolTaskData;
  42. ThreadPoolTask(Object tasks)
  43. {
  44. this.threadPoolTaskData = tasks;
  45. }
  46. public void run()
  47. {
  48. // 处理一个任务,这里的处理方式太简单了,仅仅是一个打印语句
  49. System.out.println(Thread.currentThread().getName());
  50. System.out.println("start .." + threadPoolTaskData);
  51. try
  52. {
  53. // //便于观察,等待一段时间
  54. Thread.sleep(consumeTaskSleepTime);
  55. }
  56. catch (Exception e)
  57. {
  58. e.printStackTrace();
  59. }
  60. threadPoolTaskData = null;
  61. }
  62. public Object getTask()
  63. {
  64. return this.threadPoolTaskData;
  65. }
  66. }

说明: 
1、在这段程序中,一个任务就是一个Runnable类型的对象,也就是一个ThreadPoolTask类型的对象。 
2、一般来说任务除了处理方式外,还需要处理的数据,处理的数据通过构造方法传给任务。
3、在这段程序中,main()方法相当于一个残忍的领导,他派发出许多任务,丢给一个叫 threadPool的任劳任怨的小组来做。
这个小组里面队员至少有两个,如果他们两个忙不过来,任务就被放到任务列表里面。
如果积压的任务过多,多到任务列表都装不下(超过3个)的时候,就雇佣新的队员来帮忙。但是基于成本的考虑,不能雇佣太多的队员,至多只能雇佣 4个。
如果四个队员都在忙时,再有新的任务,这个小组就处理不了了,任务就会被通过一种策略来处理,我们的处理方式是不停的派发,直到接受这个任务为止(更残忍!呵呵)。
因为队员工作是需要成本的,如果工作很闲,闲到 3SECONDS都没有新的任务了,那么有的队员就会被解雇了,但是,为了小组的正常运转,即使工作再闲,小组的队员也不能少于两个。
4、通过调整 produceTaskSleepTime和 consumeTaskSleepTime的大小来实现对派发任务和处理任务的速度的控制,改变这两个值就可以观察不同速率下程序的工作情况。
5、通过调整4中所指的数据,再加上调整任务丢弃策略,换上其他三种策略,就可以看出不同策略下的不同处理方式。

线程池系列二:ThreadPoolExecutor讲解的更多相关文章

  1. 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分

    这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...

  2. java线程池系列(1)-ThreadPoolExecutor实现原理

    前言 做java开发的,一般都避免不了要面对java线程池技术,像tomcat之类的容器天然就支持多线程. 即使是做偏后端技术,如处理一些消息,执行一些计算任务,也经常需要用到线程池技术. 鉴于线程池 ...

  3. 《java.util.concurrent 包源码阅读》11 线程池系列之ThreadPoolExecutor 第一部分

    先来看ThreadPoolExecutor的execute方法,这个方法能体现出一个Task被加入到线程池之后都发生了什么: public void execute(Runnable command) ...

  4. 《java.util.concurrent 包源码阅读》12 线程池系列之ThreadPoolExecutor 第二部分

    接着说worker线程是如何工作的.ThreadPoolExecutor有一个成员类叫Worker,所起到的作用就是线程池worker线程的作用. private final class Worker ...

  5. 线程池系列二:一张动图,彻底懂了execute和submit

    ​ 我们知道线程池通过execute方法执行提交的Runnable任务,但Runnable只是执行任务,没有返回任何信息. [线程池原理:线程池原来是个外包公司,打工人我悟了] 若是我们想在异步执行完 ...

  6. juc线程池原理(二):ThreadPoolExecutor的成员变量介绍

    概要 线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析ThreadPoolExecutor类,来了解线程池的原理. ThreadPoolExecutor数据结构 Thread ...

  7. Java 线程池(二)

    简介 在上篇 Java 线程池(一) 我们介绍了线程池中一些的重要参数和具体含义,这篇我们看一看在 Java 中是如何去实现线程池的,要想用好线程池,只知其然是远远不够的,我们需要深入实现源码去了解线 ...

  8. 线程池系列三:ThreadPoolExecutor讲解

    三.一个用队列处理线程池例子 package demo; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; ...

  9. Java多线程系列--“JUC线程池”03之 线程池原理(二)

    概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...

随机推荐

  1. BZOJ 2794 [Poi2012]Cloakroom(离线+背包)

    2794: [Poi2012]Cloakroom Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 406  Solved: 241[Submit][St ...

  2. php include 和require的区别与转码

    php include 和require的区别相同点:include和require 都能把另外一个文件包含到当前文件中.  不同点:使用include时,当包含的文件不存在时,系统会报出警告级别的错 ...

  3. [ZJOI2008]骑士(基环树,树形dp)

    [ZJOI2008]骑士 题目描述 Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬. 最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的 ...

  4. 【BZOJ 1083】 [SCOI2005]繁忙的都市

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 很明显的最小生成树了. 输出最后选的那条边就好了. [代码] #include <bits/stdc++.h> usin ...

  5. [置顶] Netty学习总结(1)——Netty入门介绍

    1.Netty是什么? Netty是一个基于JAVA NIO类库的异步通信框架,它的架构特点是:异步非阻塞.基于事件驱动.高性能.高可靠性和高可定制性. 2.使用Netty能够做什么? 开发异步.非阻 ...

  6. ListCtrl 控件数据动态改动

    參考文章:ListCtrl 控件数据动态改动 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMDIzNjU1MA==/font/5a6L5L2T/f ...

  7. Fragmen直接来回切换deno

    思路: 第一步.建立一个activity.用来管理fragment. 第二步'获取fragmentManger 和fragmentTraction. private FragmentManager f ...

  8. C#获取本机内网外网IP

    using System.Net; # region 获取内.外网Ip /// <summary> /// 获取本地ip地址,优先取内网ip /// </summary> pu ...

  9. Hello World FastCGI

    什么是FastCGI,google吧,測试一个用C++实现的FastCGI程序. 1, Nginx 安装.http://nginx.org/en/download.html.下载解压.configur ...

  10. vue2.0 路由学习笔记

    昨天温故了一下vue2.0的路由 做个笔记简单记录一下! 1.首相和vue1.0一样 要使用vuejs的路由功能需要先引入vue-router.js 2.然后修改原有a标签处代码 这里以一个ul li ...