ThreadPool有界、无界队列以及5大参数

2024-03-24 09:30

本文主要是介绍ThreadPool有界、无界队列以及5大参数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

代码放在了github上:https://github.com/singgel/NettyDemo/tree/master/src/test/java/com/singgel

Java提供了4钟线程池:

newCachedThreadPool

newFixedThreadPool

newSingleThreadExecutor

newScheduledThreadPool

你可以通过Executors来实例化这四种线程池。

查看源码会发现,这四种线程池都直接或者间接获取的ThreadPoolExecutor实例 ,只是实例化时传递的参数不一样。所以如果java提供的四种线程池满足不了我们的需求,我们可以创建自定义线程池。

ThreadPoolExecutor的构造方法如下:


其中:
corePoolSize: 核心池的大小。 当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中
maximumPoolSize: 线程池最大线程数,它表示在线程池中最多能创建多少个线程;
keepAliveTime: 表示线程没有任务执行时最多保持多久时间会终止。
unit: 参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:

 
  1. TimeUnit.DAYS; //天

  2. TimeUnit.HOURS; //小时

  3. TimeUnit.MINUTES; //分钟

  4. TimeUnit.SECONDS; //秒

  5. TimeUnit.MILLISECONDS; //毫秒

  6. TimeUnit.MICROSECONDS; //微妙

  7. TimeUnit.NANOSECONDS; //纳秒

workQueue: 一个阻塞队列,用来存储等待执行的任务。 一般来说,这里的阻塞队列有以下几种选择:

 
  1. ArrayBlockingQueue;

  2. LinkedBlockingQueue;

  3. SynchronousQueue

threadFactory: 线程工厂,主要用来创建线程;
handler: 表示当拒绝处理任务时的策略,有以下四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:只要线程池不关闭,该策略直接在调用者线程中,运行当前被丢弃的任务
个人认为这4中策略不友好,最好自己定义拒绝策略,实现RejectedExecutionHandler接口

线程池几个参数的理解:

 

比如去火车站买票, 有10个售票窗口, 但只有5个窗口对外开放. 那么对外开放的5个窗口称为核心线程数, 而最大线程数是10个窗口.如果5个窗口都被占用, 那么后来的人就必须在后面排队, 但后来售票厅人越来越多, 已经人满为患, 就类似于线程队列已满.这时候火车站站长下令, 把剩下的5个窗口也打开, 也就是目前已经有10个窗口同时运行. 后来又来了一批人,10个窗口也处理不过来了, 而且售票厅人已经满了, 这时候站长就下令封锁入口,不允许其他人再进来, 这就是线程异常处理策略.而线程存活时间指的是, 允许售票员休息的最长时间, 以此限制售票员偷懒的行为.

以下主要讲解存储等待执行的任务的队列对线程池执行的影响。

一.有界队列

1.初始的poolSize < corePoolSize,提交的runnable任务,会直接做为new一个Thread的参数,立马执行 。

2.当提交的任务数超过了corePoolSize,会将当前的runable提交到一个block queue中,。

3.有界队列满了之后,如果poolSize < maximumPoolsize时,会尝试new 一个Thread的进行救急处理,立马执行对应的runnable任务。

4.如果3中也无法处理了,就会走到第四步执行reject操作。

package com.singgel.threadPool;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class BoundedThreadPool implements Runnable {public String name;public BoundedThreadPool(String name) {this.name = name;}@Overridepublic void run() {System.out.println(name);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(3);ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, //核心池的大小。 当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中2,	//线程池最大线程数,它表示在线程池中最多能创建多少个线程1L,  //表示线程没有任务执行时最多保持多久时间会终止///TimeUnit.DAYS;               //天//TimeUnit.HOURS;             //小时//TimeUnit.MINUTES;           //分钟//TimeUnit.SECONDS;           //秒//TimeUnit.MILLISECONDS;      //毫秒//TimeUnit.MICROSECONDS;      //微妙//TimeUnit.NANOSECONDS;       //纳秒TimeUnit.SECONDS,  //参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:///ArrayBlockingQueue;//LinkedBlockingQueue;//SynchronousQueueworkQueue //一个阻塞队列,用来存储等待执行的任务);threadPool.execute(new BoundedThreadPool("任务1"));threadPool.execute(new BoundedThreadPool("任务2"));threadPool.execute(new BoundedThreadPool("任务3"));threadPool.execute(new BoundedThreadPool("任务4"));threadPool.execute(new BoundedThreadPool("任务5"));threadPool.execute(new BoundedThreadPool("任务6"));threadPool.shutdown();}}
执行结果是:且线程是两个两个执行的。
分析:线程池的corePoolSize为1,任务1提交后,线程开始执行,corePoolSize 数量用完,接着任务2、3、4提交,放到了有界队列中,此时有界队列也满了。继续提交任务5,由于当前运行的线程数poolSize < maximumPoolsize,线程池尝试new一个新的线程来执行任务5,所以任务5会接着执行。当继续提交任务6,时,poolSize达到了maximumPoolSize,有界队列也满了,所以线程池执行了拒绝操作。

二.无界队列

与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。当有新的任务到来,系统的线程数小于corePoolSize时,则新建线程执行任务。当达到corePoolSize后,就不会继续增加,若后续仍有新的任务加入,而没有空闲的线程资源,则任务直接进入队列等待。若任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存。

package com.singgel.threadPool;import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class UnBoundedThreadPool implements Runnable {public Integer count;public UnBoundedThreadPool(Integer count) {this.count = count;}@Overridepublic void run() {System.out.println("任务" + count);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) throws InterruptedException {BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();ThreadPoolExecutor pool = new ThreadPoolExecutor(5,10,1L,TimeUnit.SECONDS,workQueue);for (int i = 1; i <= 20; i++) {pool.execute(new UnBoundedThreadPool(i));}Thread.sleep(1000);System.out.println("线程池中队列中的线程数量:" + workQueue.size());pool.shutdown();}
}


执行结果为:

如果修改了线程池的maximumPoolSize参数(大于corePoolSize的大小),程序执行结果不受影响。所以对于无界队列,maximumPoolSize的设置设置的再大对于线程的执行是没有影响的。Ps:这里说LinkedBlockingQueue是无界队列是不恰当的,只不过如果用无参构造函数初始化,默认的容量是Integer.MAX_VALUE

总结:

可以用以下一句总结:
当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略。

这篇关于ThreadPool有界、无界队列以及5大参数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/841177

相关文章

SpringBoot请求参数接收控制指南分享

《SpringBoot请求参数接收控制指南分享》:本文主要介绍SpringBoot请求参数接收控制指南,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring Boot 请求参数接收控制指南1. 概述2. 有注解时参数接收方式对比3. 无注解时接收参数默认位置

Python使用getopt处理命令行参数示例解析(最佳实践)

《Python使用getopt处理命令行参数示例解析(最佳实践)》getopt模块是Python标准库中一个简单但强大的命令行参数处理工具,它特别适合那些需要快速实现基本命令行参数解析的场景,或者需要... 目录为什么需要处理命令行参数?getopt模块基础实际应用示例与其他参数处理方式的比较常见问http

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

Redis消息队列实现异步秒杀功能

《Redis消息队列实现异步秒杀功能》在高并发场景下,为了提高秒杀业务的性能,可将部分工作交给Redis处理,并通过异步方式执行,Redis提供了多种数据结构来实现消息队列,总结三种,本文详细介绍Re... 目录1 Redis消息队列1.1 List 结构1.2 Pub/Sub 模式1.3 Stream 结

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

SpringMVC获取请求参数的方法

《SpringMVC获取请求参数的方法》:本文主要介绍SpringMVC获取请求参数的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下... 目录1、通过ServletAPI获取2、通过控制器方法的形参获取请求参数3、@RequestParam4、@

SpringKafka错误处理(重试机制与死信队列)

《SpringKafka错误处理(重试机制与死信队列)》SpringKafka提供了全面的错误处理机制,通过灵活的重试策略和死信队列处理,下面就来介绍一下,具有一定的参考价值,感兴趣的可以了解一下... 目录引言一、Spring Kafka错误处理基础二、配置重试机制三、死信队列实现四、特定异常的处理策略五

Spring Boot项目部署命令java -jar的各种参数及作用详解

《SpringBoot项目部署命令java-jar的各种参数及作用详解》:本文主要介绍SpringBoot项目部署命令java-jar的各种参数及作用的相关资料,包括设置内存大小、垃圾回收... 目录前言一、基础命令结构二、常见的 Java 命令参数1. 设置内存大小2. 配置垃圾回收器3. 配置线程栈大小

SpringBoot利用@Validated注解优雅实现参数校验

《SpringBoot利用@Validated注解优雅实现参数校验》在开发Web应用时,用户输入的合法性校验是保障系统稳定性的基础,​SpringBoot的@Validated注解提供了一种更优雅的解... 目录​一、为什么需要参数校验二、Validated 的核心用法​1. 基础校验2. php分组校验3

一文带你了解SpringBoot中启动参数的各种用法

《一文带你了解SpringBoot中启动参数的各种用法》在使用SpringBoot开发应用时,我们通常需要根据不同的环境或特定需求调整启动参数,那么,SpringBoot提供了哪些方式来配置这些启动参... 目录一、启动参数的常见传递方式二、通过命令行参数传递启动参数三、使用 application.pro