线程池(一)

news2024/11/24 19:00:17

个人博客地址:
http://xiaohe-blog.top/index.php/archives/14/

文章目录

  • 1. 为什么要使用线程池
  • 2. Executor
  • 3. ThreadPoolExecutor
    • 3.1 七个参数
    • 3.2 任务队列
    • 3.3 拒绝策略
  • 4. 创建线程池
  • 5. Executors
    • 5.1 CachedThreadPool
    • 5.2 FixedThreadPool
    • 5.3 SingleThreadExecutor
    • 5.4 ScheduledThreadPoolExecutor
  • 6. ExecutorService

1. 为什么要使用线程池

复习一下创建线程的几种方式:

  • 继承Thread
  • 实现Runnable
  • 实现Callable

但是如果频繁的创建/销毁线程,就会造成资源浪费。这时候就需要将线程创建好之后存起来,以后要用取出来,用完后再放回去。

注意 :线程池并不是一种新的线程创建方法,它也是靠上面的方式创建线程的,只不过它能保存线程,实现线程资源的重复利用。

为什么要学线程池?效率高?牛逼?错,面试喜欢问😁

2. Executor

创建之前首先要来认识一下它 :Executor

Executor是线程池的根接口

image-20221224171954273

但是使用时大部分使用ThreadPoolExecutor

3. ThreadPoolExecutor

3.1 七个参数

在创建线程池之前先来看看它的7个参数:

new ThreadPoolExecutor(int corePoolSize, 
                       int maximumPoolSize,
                       long keepAliveTime,
                       TimeUnit unit,
                       BlockingQueue<Runnable> workQueue,
                       ThreadFactory threadFactory,
                       RejectedExecutionHandler handler)
参数类型参数名解释
intcorePoolSize核心线程数量
intmaximumPoolSize最大线程数
longkeepAliveTime最大空闲时间
TimeUnitunit时间单位
BlockingQueueworkQueue任务队列
ThreadFactorythreadFactory线程工厂
RejectedExecutionHandlerhandler饱和处理机制
  • corePoolSize

    线程池核心线程数。

    默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制,除非将allowCoreThreadTimeOut设置为true

  • maximumPoolSize

    线程池所能容纳的最大线程数。超过maximumPoolSize的线程将被阻塞。

    最大线程数maximumPoolSize不能小于corePoolSize

  • keepAliveTime

    临时线程的闲置超时时间。超过这个时间临时线程就会被回收。

    即 :核心线程被创建后一直存活,临时线程如果在闲置时间(keepAliveTime)内没有接到新任务会被销毁。

  • TimeUnit

    keepAliveTime的时间单位,如TimeUnit.SECONDS。表示:keepAliveTime的单位是秒

  • workQueue

    线程池中的任务队列。

    没有获得线程资源的任务将会被放入workQueue,等待线程资源被释放。

    • 如果核心线程数满了,新来的任务会首先放入任务队列

    • 如果任务队列满了,会额外创建临时线程完成任务

    • 如果线程数到达最大线程数时任务队列还是满的,多余的任务将由RejectedExecutionHandler的拒绝策略进行处理。

    常用的有三种队列: SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue

  • threadFactory

    提供创建新线程功能的线程工厂。通过threadFactory程序员可以自定义线程池中线程的创建方法。

    ThreadFactory是一个接口,只有一个newThread方法:

    Thread newThread(Runnable r);
    
  • rejectedExecutionHandler

    拒绝策略,无法被线程池处理的任务的处理器。

    一般是因为任务数超出了workQueue的容量。

3.2 任务队列

常用的三个队列 :

SynchronousQueue :无界缓冲等待队列。没有容量,是不储存元素的阻塞队列,会直接将任务交给线程,必须等队列中的添加元素表内消费后才能继续添加新的元素。即 :每次只能装一个任务,被线程拿走后才能继续接收任务。使用SynchronousQueue阻塞队列一般要求最大线程数为无界,避免线程拒绝执行操作。

LinkedBlockingDeque :无界缓冲等待队列。当前执行的线程数量达到核心线程数时,剩余的元素会在阻塞队列里等待,即一次性拿走所有任务,有多少新来的也能装入,等待线程一个一个执行。(该队列默认大小为Integer.MAX_VALUE)

ArrayBlockingQueue :有界缓存等待队列。有容量,可以指定缓存队列的大小。只能装指定容量的任务,被线程拿走后才能继续装。

3.3 拒绝策略

常用的四个拒绝策略 :(它们都是ThreadPoolExecutor的内部类)

AbortPolicy :丢弃多余任务,抛出异常。默认策略。

CallerRunsPolicy :不抛弃任务,创建线程池的线程帮忙执行任务(例如main)。

DiscardPolicy :丢弃新任务,不抛出异常。假如新来5个任务想进入队列,丢弃他们。

DiscardOldestPolicy :丢弃旧任务,不抛出异常。假如新来5个任务想进入队列,将任务队列中的前五个丢弃,新来的进入队列。

4. 创建线程池

现在就用刚才所学的知识创建一个线程池并让它执行一些任务。

首先创建一个任务类,处理响应任务

public class Task implements Runnable{
    public String name;

    public Task(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        System.out.println("线程: " + name + " 执行任务");
    }
}

紧接着就可以创建线程池,这里我们创建一个 :

核心线程数为2,最大线程数为5,存活时间为10s,队列使用ArrayBlockingQueue,拒绝策略使用CallerRunsPolicy(创建线程池的线程帮忙执行多余任务)

让它处理20个任务 :

public class Main {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2,
                5,
                10,
                TimeUnit.SECONDS, 
                new ArrayBlockingQueue<Runnable>(5),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
        for (int i = 0; i < 20; i++) {
            try {
                executor.execute(new Task(i + ""));
            } catch (Throwable e) {
                System.out.println("丢弃任务: " + (i));
            }
        }
    }
}

执行后可以看到: main确实帮忙处理任务了。

image-20221224191243300

但是也可以注意到,任务已经执行结束,但是程序还未关闭,这就证明线程池并不会主动关闭,需要我们调用executor.shutdown()关闭并回收线程池。

image-20221224191427978

5. Executors

Java提供了Executors工具类来创建线程池,注意它只是一个工具类,就像Strings是String的工具类、Objects是Object的工具类一样。Executors创建的线程池都是ExecutorService的实现类。

但是Executors这种方式创建的线程池是JDK提供的,有很多细节无法把控,所以阿里规范禁止使用Executors来创建线程池。

【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:
Executors返回的线程池对象的弊端如下:
1)FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

但是这里依旧要说一下这几种线程池,毕竟是官方封装的。而且后面自己写demo也可以直接用。

这几种线程池也是通过new ThreadPoolExecutor() 创建的,只不过其中的参数不同,所实现的功能也不同。

5.1 CachedThreadPool

Executors.newCachedThreadPool()

可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

核心线程数为0,任务队列只能装一个任务。也就是 只要来一个任务就创建一个线程处理它,来多少任务就创建多少线程。这些线程有60s的存活时间,如果接不到新任务就会被回收。

比起手动new线程,这个线程池可以实现线程复用、线程超时回收。但是可能创建太多线程造成资源浪费。

5.2 FixedThreadPool

Executors.newFixedThreadPool(int nThread)

定长线程池,可控制最大并发线程数量,超出的线程会在队列中等待。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

核心线程数 = 最大线程数,核心线程数满了之后任务在队列中等待,队列满了之后直接执行拒绝策略,而不是创建临时线程,因为没有线程。

5.3 SingleThreadExecutor

Executors.newSingleThreadExecutor()

单例线程池 :只会用唯一的线程工作,保证所有任务按照FIFL、LIFO优先级执行。

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

核心线程数 = 最大线程数 = 1,只有一个线程,所有任务都由它处理,任务队列LinkedBlockingQueue可以一次性装很多任务,这些任务排着队等待唯一的线程执行。

5.4 ScheduledThreadPoolExecutor

调度线程池,定长,支持定时及周期性任务执行,延迟任务执行。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
------
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

可以指定核心线程数,最大线程数是Integer.MAX_VALUE,可以实现延时执行、周期执行。

延时执行

delay 延时时间。

// 参数 : 任务 + 时间 + 时间单位
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                       long delay, TimeUnit unit);

周期执行

initialDelay 第一次执行的延时时间

period 每一次执行的间隔时间。

// 参数 : 任务 + 时间 + 时间单位
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                              long initialDelay,
                                              long period,
                                              TimeUnit unit);

6. ExecutorService

刚刚说了,Executors创建的都是ExecutorService的子类,那么现在来使用一下ExecutorService完成任务。

先来看看它拥有的方法:

image-20221225150655310

  • shutdown :任务执行完毕后销毁线程池。
  • shutdownNow :立即销毁线程池。
  • invokeAll :执行所有任务。
  • submit :提交并执行任务。
public class TestExecutorService {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executorService.submit(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行");
            });
        }
        
        System.out.println(executorService.isShutdown());
        executorService.shutdown();
        System.out.println(executorService.isShutdown());
    }
}

执行结果为 :

pool-1-thread-2 执行
pool-1-thread-5 执行
pool-1-thread-6 执行
pool-1-thread-4 执行
pool-1-thread-1 执行
pool-1-thread-3 执行
false
pool-1-thread-8 执行
pool-1-thread-7 执行
pool-1-thread-9 执行
pool-1-thread-4 执行
true

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/114697.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SAP UI5 应用里一些容器控件的介绍

sap.m.Shell 控件可用作应用程序的根元素。 它可以包含 App 或 SplitApp 控件。 Shell 为整个应用程序提供了一些总体功能&#xff0c;并负责在桌面浏览器平台上进行视觉适配&#xff0c;例如应用程序周围的框架。 sap.m.App: 该 App 继承自 sap.m.NavContainer 并因此提供其导…

VUEElement 学习笔记

1 VUE 1.1 示例 新建test_vue.jsp <% page contentType"text/html;charsetUTF-8" language"java" %> <html> <head><title>Title</title> </head> <body><div id"app"><input name"…

三、SpringBoot启动流程及自动化配置

一、Springboot启动流程 图一:Springboot项目的启动流程 首先,针对上图中自己不太明确的两个知识点,这里做如下总结: 1.Banner:参考这篇文章:SpringBoot之Banner介绍 - MarkLogZhu - 博客园 (cnblogs.com) ; 2.钩子方…

【Javassist】快速入门系列07 当检测到字段被访问时使用语句块替换访问

系列文章目录 01 在方法体的开头或结尾插入代码 02 使用Javassist实现方法执行时间统计 03 使用Javassist实现方法异常处理 04 使用Javassist更改整个方法体 05 当有指定方法调用时替换方法调用的内容 06 当有构造方法调用时替换方法调用的内容 07 当检测到字段被访问时使用语…

一个最基本的lLinux驱动开发框架和编译驱动方式

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、编写驱动文件1.相关头文件2.驱动入口 &出口3.申明完整代码二、编译驱动的方式三、编译驱动1. 和内核一起编译&#xff1a;2. 编译成驱动模块&#xff1a…

Kafka消息写入流程

Kafka消息写入流程 0,写入消息简要流程图 1,从示例开始 在Kafka中,Producer实例是线程安全的,通常一个Producer的进程只需要生成一个Producer实例. 这样比一个进程中生成多个Producer实例的效率反而会更高. 在Producer的配置中,可以配置Producer的每个batch的内存缓冲区的大小…

如何下载最新的NDVI数据?需要翻墙,安装MRT,如何处理下载的NDVI数据?

一、下载NDVI数据 参照网站&#xff1a; https://www.zhihu.com/question/48176218 1、先在网站https://modis.gsfc.nasa.gov/data/dataprod/mod13.php查看要下载的数据 其实没什么要看的&#xff0c;就看下面这个图就可以了&#xff0c;根据下图找到自己要下载的数据简称&am…

Python实现猎人猎物优化算法(HPO)优化支持向量机分类模型(SVC算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 猎人猎物优化搜索算法(Hunter–prey optimizer, HPO)是由Naruei& Keynia于2022年提出的一种最新的优…

Hibernate-Validator的使用(一)

文章目录学习建议全部的约束注解关于NotEmpty和NotBlank的注意事项关于Size的注意事项约束注解可以放的位置JavaBeanValidator接口约束注解的继承性普通方法和构造方法入参和返回值ExecutableValidator 接口约束注解的继承性错误信息国际化显示定义message使用的消息key定义Val…

模仿现实生活中的通讯录(2)

距离第一篇已经过去很久&#xff0c;我之所以暂时放下通讯录&#xff0c;是因为学业颇多&#xff0c;无暇顾及。现在放假已经有一段时间了&#xff0c;脱离每天忙碌的生活后&#xff0c;我只想享受一下整日无事&#xff0c;浑浑噩噩过一天的感觉&#xff0c;只不过差点没收得住…

leetcode150/155. 逆波兰表达式求值、最小栈;剑指 Offer 31. 栈的压入、弹出序列

目录 题目链接与简介 题目一 1.算法思路 2.总结心得 3.可执行代码 题目二 1.算法思路 2.总结心得 3.可执行代码 题目三 1.算法思路 2.总结心得 3.可执行代码 题目链接与简介 1.逆波兰表达式求值 2.最小栈 3.剑指 Offer 31. 栈的压入、弹出序列 题目一 LeetCod…

软件测试 -- 进阶 8 软件测试流程和过程

盖天下之事&#xff0c;不难于立法&#xff0c;而难于法之必行&#xff1b;不难于听言&#xff0c;而难于言之必效。-- 明 张居正 释译&#xff1a;天下大事&#xff0c;困难的不在于要制定什么法律&#xff0c;而在于立了法就一定要执行&#xff1b;重要的不在于说什么&a…

【实时数仓】DWS层之地区主题表(FlinkSQL)、关键词主题表(FlinkSQL)

文章目录一 DWS层-地区主题表(FlinkSQL)1 分组开窗和聚合计算&#xff08;1&#xff09;分组窗口&#xff08;2&#xff09;选择分组窗口的开始和结束时间戳&#xff08;3&#xff09;系统内置函数&#xff08;4&#xff09;完整代码2 将动态表转换为流&#xff0c;写入ClickHo…

计算机基础(五):C语言的程序的处理过程

一段C语言程序 打开任何一个C语言的教程&#xff0c;首先自然是展示一段 Hello World 的程序&#xff0c;类似于下面这段&#xff1a; #include <stdio.h>int main() {/* 我的第一个 C 程序 */printf("Hello, World! \n");return 0; }运行上面这段程序也很简…

Qt中的数据库(简单使用)

在Qt中支持对数据库的操作 Qt中数据库的类有&#xff1a; 驱动层&#xff1a;为具体的数据库和SQL接口层之间提供底层的桥梁SQL层&#xff1a;提供对数据库的访问 QSqlDateBase类用来创建连接QSqlQuery可以使用SQL语句实现交互用户接口层&#xff1a;实现将数据库中的数据链接…

36. 卷积神经网络(LeNet)

通过之前几节&#xff0c;我们学习了构建一个完整卷积神经网络的所需组件。 回想一下&#xff0c;之前我们将softmax回归模型和多层感知机模型应用于Fashion-MNIST数据集中的服装图片&#xff0c;为了能够应用softmax回归和多层感知机&#xff1a; 我们首先将每个大小为的图像…

【Web开发】Python实现Web服务器(Ubuntu下Flask使用MySQL数据库)

&#x1f37a;基于Python的Web服务器系列相关文章编写如下&#x1f37a;&#xff1a; &#x1f388;【Web开发】Python实现Web服务器&#xff08;Flask快速入门&#xff09;&#x1f388;&#x1f388;【Web开发】Python实现Web服务器&#xff08;Flask案例测试&#xff09;&a…

大数据-玩转数据-Kafka实战

一、kafka使用要点 要点一&#xff1a;Producer即生产者&#xff0c;向Kafka集群发送消息&#xff0c;在发送消息之前&#xff0c;会对消息进行分类&#xff0c;即Topic&#xff0c;topic1&#xff0c;topic2。Topic即主题&#xff0c;通过对消息指定主题可以将消息分类&#…

工具-能写会看正则表达式【强烈建议收藏!】

正则表达式,很常用的一个技能点,但是一般的开发流程中都是这样的: 需要验证数据上网搜一下正则表达式CV 搞定!!!今天有时间回看了一下文档,简单整理了一下里面需要注意的点,并且通过分析几个常见的正则表达式,下次遇到正则争取不再只依靠 CV 大法! 基础部分 基本语法 …

初识指针(9)

目录 1、指针是什么&#xff1f; 2、指针和指针类型 1、指针- 整数 2、指针的解引用 3、野指针 1、野指针成因 2、如何规避野指针 4、指针运算 1、指针- 整数 2、指针- 指针 3、指针的关系运算 5、指针和数组 6、二级指针 7、指针数组 1、指针是什么&#xff1f;…