为了更好的使用多线程,JDK提供了线程池供开发人员使用,目的在于减少线程的创建和销毁次数,以此达到线程的重复利用。

其中ThreadPoolExecutor是线程池中最核心的一个类,我们先简单看一下这个类的继承关系。

其中Executor是线程池的顶级接口,接口中只定义了一个方法  void execute(Runnable command);线程池的操作方法都是定义子在ExecutorService子接口中的,所以说ExecutorService是线程池真正的接口。

ThreadPoolExecutor提供了四个构造方法,我们看一下参数最全的一个构造函数;

public ThreadPoolExecutor(int corePoolSize,
           int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
                  ThreadFactory threadFactory,
RejectedExecutionHandler handler) { }

函数的参数含义如下:

  • corePoolSize: 线程池核心线程数
  • maximumPoolSize:线程池最大数
  • keepAliveTime: 空闲线程存活时间
  • unit: 时间单位
  • workQueue: 线程池所使用的缓冲队列
  • threadFactory:线程池创建线程使用的工厂
  • handler: 线程池对拒绝任务的处理策略

本节我们主要对前五个参数中的corePoolSize,maximumPoolSize及workQueue是如何配合使用做出说明(keepAliveTime,unit主要对空闲线程的存活时间做的定义,见名知意,不再做出说明),以此来引出线程池的一些特性。

threadFactory和handler这两个参数都有默认值,对于它们的用法将放到其它章节去做说明。

特性一:当池中正在运行的线程数(包括空闲线程)小于corePoolSize时,新建线程执行任务。

下面用实验来说明,代码如下:

public class TestThreadPoolExecutor {

    public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1));
//任务1
pool.execute(new Runnable() { @Override
public void run() {
System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName());
}
}); try {
//主线程睡2秒
Thread.sleep(2*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//任务2
pool.execute(new Runnable() { @Override
public void run() {
System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName());
}
}); } }

实验结果如下:

实验结果分析:

从实验结果上可以看出,当执行任务1的线程(thread-1)执行完成之后,任务2并没有去复用thread-1而是新建线程(thread-2)去执行任务。

特性二:当池中正在运行的线程数大于等于corePoolSize时,新插入的任务进入workQueue排队(如果workQueue长度允许),等待空闲线程来执行

下面用实验来说明,代码如下:

public class TestThreadPoolExecutor {

    public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1));
// 任务1
pool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3 * 1000);
System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 任务2
pool.execute(new Runnable() { @Override
public void run() {
try {
Thread.sleep(5 * 1000);
System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}); // 任务3
pool.execute(new Runnable() { @Override
public void run() {
System.out.println("-------------helloworld_003---------------" + Thread.currentThread().getName());
}
}); } }

实验结果如下:

实验结果分析:

从实验结果上看,任务3会等待任务1执行完之后,有了空闲线程,才会执行。并没有新建线程执行任务3,这时maximumPoolSize=3这个参数不起作用

特性三:当队列里的任务数达到上限,并且池中正在运行的线程数小于maximumPoolSize对于新加入的任务,新建线程。

下面用实验来说明,代码如下:

public class TestThreadPoolExecutor {

    public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1));
// 任务1
pool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3 * 1000);
System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 任务2
pool.execute(new Runnable() { @Override
public void run() {
try {
Thread.sleep(5 * 1000);
System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}); // 任务3
pool.execute(new Runnable() { @Override
public void run() {
System.out.println("-------------helloworld_003---------------" + Thread.currentThread().getName());
}
}); // 任务4
pool.execute(new Runnable() { @Override
public void run() {
System.out.println("-------------helloworld_004---------------" + Thread.currentThread().getName());
}
}); } }

实验结果如下:

实验结果分析:

当任务4进入队列时发现队列的长度已经到了上限,所以无法进入队列排队,而此时正在运行的线程数(2)小于maximumPoolSize所以新建线程执行该任务。

特性四:当队列里的任务数达到上限,并且池中正在运行的线程数等于maximumPoolSize对于新加入的任务,执行拒绝策略(线程池默认的拒绝策略是抛异常)。

下面用实验来说明,代码如下:

public class TestThreadPoolExecutor {

    public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1));
// 任务1
pool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3 * 1000);
System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 任务2
pool.execute(new Runnable() { @Override
public void run() {
try {
Thread.sleep(5 * 1000);
System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}); // 任务3
pool.execute(new Runnable() { @Override
public void run() {
System.out.println("-------------helloworld_003---------------" + Thread.currentThread().getName());
}
}); // 任务4
pool.execute(new Runnable() { @Override
public void run() {
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-------------helloworld_004---------------" + Thread.currentThread().getName());
}
}); // 任务5
pool.execute(new Runnable() { @Override
public void run() {
System.out.println("-------------helloworld_005---------------" + Thread.currentThread().getName());
}
}); } }

实验结果如下:

实验结果分析:

当任务5加入时,队列达到上限,池内运行的线程数达到最大,故执行默认的拒绝策略,抛异常。

本文中使用到的队列类型虽然仅限于LinkedBlockingQueue这一种队列类型,但总结出来的特性,对与常用ArrayBlockingQueue 和 SynchronousQueue同样适用,些许不同及三种队列的区别,将在下个章节中说明。

最后说一点,我们作为程序员,研究问题还是要仔细深入一点的。当你对原理了解的有够透彻,开发起来也就得心应手了,很多开发中的问题和疑惑也就迎刃而解了,而且在面对其他问题的时候也可做到触类旁通。当然在开发中没有太多的时间让你去研究原理,开发中要以实现功能为前提,可等项目上线的后,你有大把的时间或者空余的时间,你大可去刨根问底,深入的去研究一项技术,为觉得这对一名程序员的成长是很重要的事情。

java线程池01-ThreadPoolExecutor构造方法参数的使用规则的更多相关文章

  1. Java线程池使用和常用参数

    多线程问题: 1.java中为什么要使用多线程使用多线程,可以把一些大任务分解成多个小任务来执行,多个小任务之间互不影像,同时进行,这样,充分利用了cpu资源. 2.java中简单的实现多线程的方式 ...

  2. Java并发编程:Java线程池核心ThreadPoolExecutor的使用和原理分析

    目录 引出线程池 Executor框架 ThreadPoolExecutor详解 构造函数 重要的变量 线程池执行流程 任务队列workQueue 任务拒绝策略 线程池的关闭 ThreadPoolEx ...

  3. 【Java 多线程】Java线程池类ThreadPoolExecutor、ScheduledThreadPoolExecutor及Executors工厂类

    Java中的线程池类有两个,分别是:ThreadPoolExecutor和ScheduledThreadPoolExecutor,这两个类都继承自ExecutorService.利用这两个类,可以创建 ...

  4. 深入理解Java线程池:ThreadPoolExecutor

    线程池介绍 在web开发中,服务器需要接受并处理请求,所以会为一个请求来分配一个线程来进行处理.如果每次请求都新创建一个线程的话实现起来非常简便,但是存在一个问题: 如果并发的请求数量非常多,但每个线 ...

  5. Java线程池定制ThreadPoolExecutor官方定制实例

    1.仍然先看构造方法:ThreadPoolExecutor构造方法 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,lon ...

  6. 学习java 线程池-1: ThreadPoolExecutor

    1. Executor 该接口内只有一个接口方法 :该方法的目的就是执行指定的 Runnable (但会不会执行,或者会不会立马执行,则不一定.因为要取决于整个线程池的状态) Executor 中文的 ...

  7. Java线程池之ThreadPoolExecutor

    前言 线程池可以提高程序的并发性能(当然是合适的情况下),因为对于没有线程的情况下,我们每一次提交任务都新建一个线程,这种方法存在不少缺陷: 1.  线程的创建和销毁的开销非常高,线程的创建需要时间, ...

  8. Java线程池使用和常用参数(待续)

    线程池怎么实现的,核心参数讲一讲? Executors是线程池的工厂类,通过调用它的静态方法如下: Executors.newCachedThreadPool(); Executors.newFixe ...

  9. 关于线程池(ThreadPoolExecutor)参数的浅析

    引子 线程池在项目中很常用,需要多个任务异步执行的地方我们都会去创建一个线程池. 我们看到 ThreadPoolExecutor源码中提供了更方便的工厂方法(Executors)使用. 提供方便应该是 ...

  10. 深入理解Java线程池:ScheduledThreadPoolExecutor

    介绍 自JDK1.5开始,JDK提供了ScheduledThreadPoolExecutor类来支持周期性任务的调度.在这之前的实现需要依靠Timer和TimerTask或者其它第三方工具来完成.但T ...

随机推荐

  1. Problem : 1412 ( {A} + {B} )

    //集合中元素是不会重复的,所以完全没有必要将两个集合合并后再进行排序,交换排序的时间效率是O(n^2),将两个集合中的元素分别排序后输出即可.输出格式也非常需要 //注意的.输出一列元素赢以cout ...

  2. Pandas与Matplotlib基础

    pandas是Python中开源的,高性能的用于数据分析的库.其中包含了很多可用的数据结构及功能,各种结构支持相互转换,并且支持读取.保存数据.结合matplotlib库,可以将数据已图表的形式可视化 ...

  3. loadrunner录制上传文件,但是回放失败

    用Loadrunner录制上传文件,脚本回放时发现,文件没有上传成功,检查脚本发现脚本中没有上传文件的路径. 脚本录制时选择的协议,如果说是socket协议,则不需要上传文件的路径,Loadrunne ...

  4. python爬微信公众号前10篇历史文章(3)-lxml&xpath初探

    理解lxml以及xpath 什么是lxml? python中用来处理XML和HTML的library.与其他相比,它能提供很好的性能, 并且它支持XPath. 具体可以查看官方文档->http: ...

  5. macOS下利用dSYM文件将crash文件中的内存地址转换为可读符号

    一.使用流程 Windows下的程序运行崩溃时,往往可以利用pdb文件快速解析出程序崩溃的具体位置,甚至可以对应到源代码的具体行数.macOS下的symbolicatecrash也具备相应的功能.对应 ...

  6. #Python3.6.2(32位) pip安装 和 pygame 环境配置

    #首先确认电脑已经安装python ,可通过在命令行下 执行 python --version确认. 1. 到 https://pypi.python.org/pypi/setuptools/ 下载 ...

  7. Java多线程:队列与阻塞队列

    1. 什么是阻塞队列 阻塞队列(BlockingQueue)是 Java 5 并发新特性中的内容,阻塞队列的接口是 java.util.concurrent.BlockingQueue,它提供了两个附 ...

  8. WCF跨域解决方法及一些零碎的东西。

    之前发过一篇随笔,说的WCF配置文件配置问题.里面也配了跨域支持,但是jsoncollback只支持Get请求,Post请求是解决不了,所以这里把真正的WCF跨域问题贴出来. 话不多说,直接帖配置文件 ...

  9. Intellij Idea下tomcat设置自动编译

    *eclipse默认tomcat下是自动完成编译:而Intellij Idea默认tomcat下不是自动完成编译,从如下开始设置: 进入"settings",如下图找到" ...

  10. JavaScript Alert 函数执行顺序问题

    * { color: #3e3e3e } body { font-family: "Helvetica Neue", Helvetica, "Hiragino Sans ...