ThreadPoolExecutor是Java语言对于线程池的实现。池化技术是一种复用资源,减少开销的技术。线程是操作系统的资源,线程的创建与调度由操作系统负责,线程的创建与调度都要耗费大量的资源,其中线程创建需要占用一定的内存,而线程的调度需要不断的切换线程上下文造成一定的开销。同时线程执行完毕之后就会被操作系统回收,这样在高并发情况下就会造成系统频繁创建线程。

为此线程池技术为了解决上述问题,使线程在使用完毕后不回收而是重复利用。如果线程能够复用,那么我们就可以使用固定数量的线程来解决并发问题,这样一来不仅节约了系统资源,而且也会减少线程上下文切换的开销。

参数

ThreadPoolExecutor的构造函数有7个,它们分别是:

corePoolSize(int):线程池的核心线程数量

maximumPoolSize(int):线程池最大线程数量

keepAliveTime(long):保持线程存活的时间

unit(TimeUnit):线程存活时间单位//加入Java开发交流君样:756584822一起吹水聊天

workQueue(BlockingQueue):工作队列,用于临时存放提交的任务

threadFactory(ThreadFactory):线程工厂,用于创建线程

handler(RejectedExecutionHandler):任务拒绝处理器,当线程池无法再接受新的任务时,会交给它处理

一般情况下,我们只使用前五个参数,剩余两个我们使用默认参数即可。

任务提交逻辑

其实,线程池创建参数都与线程池的任务提交逻辑密切相关。根据源码描述可以得知:当提交一个新任务时(执行线程池的execute方法)会经过三个步骤的处理。

当任务数量小于corePoolSize时,线程池会创建一个新的线程(创建新线程由传入参数threadFactory完成)来处理任务,哪怕线程池中有空闲线程,依然会选择创建新线程来处理。

当任务数量大于corePoolSize时,线程池会将新任务压入工作队列(参数中传递的workQueue)等待调度。

当新提交的任务无法压入工作队列时,会检查当前任务数量是否大于maximumPoolSize。如果小于maximunPoolSize则会新建线程来处理任务(这时我们的keepAliveTime参数就起作用了,它主要作用于这种情况下创建的线程,如果任务数量减小,这些线程闲置了,那么在超过keepAliveTime时间后就会被回收)。如果大于了maximumPoolSize就会交由任务拒绝处理器handler处理。

线程池状态

正如线程有不同的状态一样,线程池也拥有不同的运行状态。源码中提出,线程池有五种状态,分别为:

RUNNING:运行状态,不断接收任务并处理它们。

SHUTDOWN:关闭状态,不接收新任务,但是会处理工作队列中排队的任务。

STOP:停止状态,不接收新任务,清空工作队列且不会处理工作队列的任务。

TIDYING:待终止状态,此状态下,任务队列和线程池都为空。

TERMINATED:终止状态,线程池关闭。

如何让线程不被销毁

文章开头说到,线程在执行完毕之后会被操作系统回收销毁,那么线程池时如何保障线程不被销毁?首先看一个测试用例:

public static void testThreadState()

{

Thread thread = new Thread(() -> System.out.println("Hello world")); // 创建一个线程

System.out.println(thread.getState()); // 此时线程的状态为NEW

thread.start(); // 启动线程,状态为RUNNING

System.out.println(thread.getState());

try

{

thread.join();

System.out.println(thread.getState()); // 线程运行结束,状态为TERMINATED

thread.start(); // 此时再启动线程会发生什么呢?

} catch (InterruptedException e)

{

e.printStackTrace();

}

}//加入Java开发交流君样:756584822一起吹水聊天

结果输出:

NEW

RUNNABLE

Hello world

TERMINATED

Exception in thread "main" java.lang.IllegalThreadStateException

at java.base/java.lang.Thread.start(Thread.java:794)

at misc.ThreadPoolExecutorTest.testThreadState(ThreadPoolExecutorTest.java:90)

at misc.ThreadPoolExecutorTest.main(ThreadPoolExecutorTest.java:114)

可以看出,当一个线程运行结束之后,我们是不可能让线程起死回生重新启动的。既然如此ThreadPoolExecutor如何保障线程执行完一个任务不被销毁而继续执行下一个任务呢?

其实这里就要讲到我们最开始传入的参数workQueue,它的接口类型为BlockingQueue,直译过来就是阻塞队列。这中队列有个特点,就是当队列为空而尝试出队操作时会阻塞。

基于阻塞队列的如上特点,ThreadPoolExecutor采用不断循环+阻塞队列的方式来实现线程不被销毁。

final void runWorker(Worker w) {

Thread wt = Thread.currentThread();

Runnable task = w.firstTask;

w.firstTask = null;

w.unlock(); // allow interrupts

boolean completedAbruptly = true;

try {

// 从工作队列中不断取任务。如果工作队列为空,那么程序会阻塞在这里

while (task != null || (task = getTask()) != null) {

w.lock();

// 检查线程池状态

if ((runStateAtLeast(ctl.get(), STOP) ||

(Thread.interrupted() &&

runStateAtLeast(ctl.get(), STOP))) &&

!wt.isInterrupted())

wt.interrupt();

try {

beforeExecute(wt, task);

try {//加入Java开发交流君样:756584822一起吹水聊天

执行任务

task.run();

afterExecute(task, null);

} catch (Throwable ex) {

afterExecute(task, ex);

throw ex;

}

} finally {

task = null;

w.completedTasks++;

w.unlock();

}

}

completedAbruptly = false;

} finally {

processWorkerExit(w, completedAbruptly);

}

}

关闭线程池

想要关闭线程池可以通过调用shutdown()和shutdownNow()方法实现。两种方法有所不同,其中调用shutdown()方法会停止接收新的任务,处理工作队列中的任务,调用这个方法之后线程池会进入SHUTDOWN状态,此方法无返回值并且不抛出异常。

而shutdownNow()方法会停止接收新的任务,而且会返回未完成的任务集合,同时这个方法也会抛出异常。

如何创建一个适应业务背景的线程池

线程池创建有七个参数,这几个参数的相互作用可以创建出适应特定业务场景的线程池。其中最为重要的有三个参数分别为:corePoolSize,maximumPoolSize,workQueue。其中前两个参数已经在上文中作了详细介绍,而workQueue参数在线程池创建中也极为重要。workQueue主要有三种:

//加入Java开发交流君样:756584822一起吹水聊天

SynchronousQueue:这个队列只能容纳一个元素,而且只有当队列为空时可以入队。

ArrayBlockingQueue:这是一个固定容量大小的队列。

LinkedBlockingQueue:链式阻塞队列,容量无限。

通过上述三种队列的特性我们可以得知,

当使用SynchronousQueue的时候,总是倾向于新建线程处理请求,如果线程池大小参数设置的很大,那么线程数量倾向于无限增长。这样的线程池能够高效处理突发增长的请求,而且处理效率很高,但是开销很大。//加入Java开发交流君样:756584822一起吹水聊天

当使用ArrayBlockingQueue的时候,线程池所能处理的瞬时最大任务量为队列大小 + 线程池最大数量,这样的线程池中规中矩,使用的业务场景很多,具体还需结合业务场景来调配三个参数的大小。例如I/O密集型的场景,多数的线程处于阻塞状态,为了提高系统吞吐量,我们希望能够有多数线程来处理IO。这样的话我们偏向于将corePoolSize设置的大一点。而且阻塞队列大小不要设置很大,同时maximumPoolSize也设置的大一点。

当使用LinkedBlockingQueue时,线程池的maximumPoolSize参数会失效,因为按照任务提交流程来看,LinkedBlockingQueue可以无限制地容纳任务,自然不会出现队列无法工作,新建线程处理的情况。使用LinkedBlockingQueue可以平稳地处理一些请求激增的情况,但是处理效率不会提高,仅仅能够起到一定的缓冲作用。

标签:Java,队列,创建,处理,任务,详解,线程,参数,ThreadPoolExecutor

来源: https://blog.csdn.net/wj1314250/article/details/113924970

java threadpoolexecutor 返回值_Java ThreadPoolExecutor详解相关推荐

  1. java list取值_Java集合详解

    一.集合的由来 通常,我们的程序需要根据程序运行时才知道创建多少个对象.但若非程序运行,程序开发阶段,我们根本不知道到底需要多少个数量的对象,甚至不知道它的准确类型.为了满足这些常规的编程需要,我们要 ...

  2. java 接口 返回值_java api返回值的标准化详解

    api返回值的标准化 例如 {"status":200,"message":"操作成功","data":"{\ ...

  3. java return返回值_java中关于return返回值的用法详解

    我们输入一个条件时,系统就会对这个条件进行判断,然后给出一个返回时的结论,我们把这个结果看做是返回值.在java里可以使用return语句来进行返回,从字面意思就能很好的理解它的用法了.下面我们就re ...

  4. java模型给泛型_java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一

    对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 1. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应 ...

  5. java泛型常用特点_Java泛型详解

    对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 1. 概述 泛型在 ...

  6. java合法的数组声明_Java数组详解

    Java数组详解 数组详解 1.数组概述 数组是相同类型数据的有序集合 每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们 2.数组声明和创建 ①数组声明形式 dataType[] a ...

  7. java 枚举使用例子_Java枚举详解及使用实例(涵盖了所有典型用法)

    在实际编程中,往往存在着这样的"数据集",它们的数值在程序中是稳定的,而且"数据集"中的元素是有限的. 例如星期一到星期日七个数据元素组成了一周的"数 ...

  8. java 1.8 泛型_Java 泛型详解

    原标题:Java 泛型详解 引言 泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用.本文我们将从零开始来看一下Java泛型的设计,将会涉及到通配符处理,以及让人苦恼的类型擦除 ...

  9. linux中grep命令返回值,grep命令详解

    如果你是一个新手,请从头阅读这篇文章,如果你只是忘记了grep命令的一些常用选项,直接查看文章尾部的总结部分即可. 先说说grep命令能做什么? 我们可以使用grep命令在文本中查找指定的字符串,就像 ...

最新文章

  1. Spring Boot 最流行的 16 条实践解读,值得收藏!
  2. CNN光流计算--FlowNet: Learning Optical Flow with Convolutional Networks
  3. STDIN_FILENO和stdin区别
  4. 【Linux】一步一步学Linux——groupmod命令(89)
  5. matlab 功率谱分析函数psd用法
  6. 【2018.3.17】模拟赛之四-ssl1864jzoj1368 燃烧木棒【最短路,Floyd】
  7. Spring Boot下无法加载主类 org.apache.maven.wrapper.MavenWrapperMain问题解决
  8. 华为成功完成中国联通NFV三层解耦测试验证
  9. java中date类型如何赋值_Java 中的类型传递问题解惑
  10. anaconda要和python安装在一个目录下吗_Python开发环境配置,Anaconda的安装!
  11. last-child 选取不到指定元素,失去效果
  12. pure-ftp 修改用户信息
  13. linux下部署node+vue文件
  14. 题目:求100以内的全部素数,每行输出10个。1不是素数
  15. linux拷贝依赖库到指定目录,Linux 批量依赖库拷贝(ldd)
  16. WS2812B全彩LED驱动
  17. 红米手机开启---开发者选项方法
  18. java连接redis设置密码_jedis设置密码连接Redis
  19. 使用经典的基本播放命令和 MML 创建 MIDI 文件
  20. MATLAB演奏音乐

热门文章

  1. grafana-大数据图表监控分析框架
  2. idea Tomcat启动项目报错 Message: 前言中不允许有内容,等其它问题
  3. 走进阿里:现任阿里技术团队负责人,公开解密阿里新一代核心技术
  4. 微信小程序视频视频背景与控制处理(笔记)
  5. 唱吧并购线下KTV:干得漂亮!
  6. html 十大常用标签,HTML 常用标签
  7. 夏季与幸福的距离?也许只是一场雨
  8. 二维码生成器 python excel_python生成excel的实例代码
  9. 在NUC972上移植pjsip库并使用PJSUA测试VOIP电话(二)
  10. Oracle Latch牛刀小试