百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

工作中如何正确使用线程池(工作中如何正确使用线程池设备)

wxin55 2024-11-07 13:13 14 浏览 0 评论

浅浅了解一下池化技术

池化技术指的是提前准备一些资源,在需要时可以重复使用这些预先准备的资源。是一种优化资源管理和提高系统性能的技术,广泛应用于需要频繁创建、使用和销毁资源的场景。其核心思想是预先创建一定数量的资源对象,并将这些对象保存在一个“池”(如线程池、连接池或对象池)中,以供重复使用,而不是每次需要时都重新创建和销毁资源。

线程池

  1. 什么是线程池

线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象。

  1. 为什么使用线程池

使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源开销,解决资源不足的问题。如果不使用线程池,有可能会造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

  • 降低资源消耗
  • 提高响应速度,方便管理
  • 线程可以复用、可以控制最大并发数、可以管理线程
  1. 有哪些线程池
  • Executors.newFixedThreadPool:创建固定线程数量的线程池
  • Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池
  • Executors.newCachedThreadPool():创建一个可根据实际情况调整线程数量的线程池
  • Executors.newScheduledThreadPool():创建可定期执行

FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。

CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。

  1. 正确使用线程池+7大参数

ThreadPoolExecutor的参数

  • int corePoolSize:核心线程数量
  • int maximumPoolSize:最大线程数
  • long keepAliveTime: 最大空闲时间
  • TimeUnit unit:时间单位
  • BlockingQueue<Runnable> workQueue:任务队列
  • ThreadFactory threadFactory:线程工厂
  • RejectedExecutionHandler handler: 饱和处理机制

工作原理:

  • 线程池在初始化的同时,会自动创建一定数量的线程(核心线程)并存放起来。这些线程在没有任务时处于空闲状态,等待任务的到来。
  • 当有新的任务需要执行时,可以通过调用线程池的execute()或submit()方法将任务提交到线程池。execute()方法用于提交无返回值的Runnable任务,而submit()方法则可以提交有返回值的Callable任务。
  • 判断核心线程是否空闲:线程池会首先判断核心线程数量是否已满。
  • 如果未满,且有空闲的核心线程,则直接分配一个空闲的核心线程来执行任务。
  • 如果核心线程都在忙碌,线程池会检查工作队列(一个阻塞队列)是否已满。
  • 如果工作队列未满,则将新任务添加到工作队列中等待执行。空闲的核心线程会从工作队列中按照先进先出(FIFO)的规则取出任务并执行。
  • 如果工作队列已满,且当前存活线程数未达到线程池的最大线程数(maximumPoolSize),线程池会创建一个新的非核心线程来执行任务
  • 如果当前存活线程数已达到最大线程数,且工作队列也已满,此时再有新任务提交,线程池会根据预设的拒绝策略来处理新任务。常见的拒绝策略包括
  • AbortPolicy:直接抛出RejectedExecutionException异常,阻止新任务的提交。
  • CallerRunsPolicy:由提交任务的线程(调用者线程)来处理该任务。
  • DiscardPolicy:直接丢弃新提交的任务,不做任何处理。
  • DiscardOldestPolicy:丢弃队列中最旧的未处理任务,然后尝试重新提交被拒绝的任务。
  • 自定义拒绝策略:通过实现RejectedExecutionHandler接口来自定义任务拒绝的处理方式。

简而言之:

使用银行案例理解:例如银行有5个窗口,对应有5个员工(最大线程数),银行大厅只有20座位(任务队列)。

重点来了:一天早上银行只开了2个窗口(2个核心线程数量),刚开始办理人比较少,突然来一波办理业务的人,大约30人。大厅的座位已经满了(任务队列满了)。行长见状不妙,处理不过来,另外安排了3名员工开了剩下的三个窗口,目前一共5个窗口(达到最大线程数)。此时5个窗口有人办理,另外座位满了。银行已经塞不下人了,里面挂牌当前办理人太多,11点过后再来(拒绝策略)

  1. 如何判断是CPU密集型任务还是IO密集型任务
  • CPU 密集型:利用 CPU 计算能力的任务比如你在内存中对大量数据进行排序。
  • IO 密集型:单凡涉及到网络读取,文件读取这类都是 IO 密集型,这类任务的特点是 CPU 计算耗费时间相比于等待 IO 操作完成的时间来说很少,大部分时间都花在了等待 IO 操作完成上。

工作中使用线程池(自定义)

Spring中实现多线程,其实非常简单,只需要在配置类中添加@EnableAsync就可以使用多线程。在希望执行的并发方法中使用@Async就可以定义一个线程任务。

  1. 定义线程池配置(使用参数有可以进行写在配置文件yml里)
@Configuration
@EnableAsync
public class ThreadPoolConfig {
  
     private final int core = Runtime.getRuntime().availableProcessors();

    @Bean(name = "testTaskExecutor")
    public ThreadPoolTaskExecutor testTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 配置核心线程数
  			executor.setCorePoolSize(this.core);
        // 配置最大线程数
       executor.setMaxPoolSize(this.core * 2);
        //配置队列大小
        executor.setQueueCapacity(100);
        //线程池维护线程所允许的空闲时间
        executor.setKeepAliveSeconds(60);
        // 配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix("test-progress-task-");
        //拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}
  1. 使用线程池
@Slf4j
@Component
public class TestSupport {

    @Async("testTaskExecutor")
    public void testTask() {
       System.out.println("异步执行")
    }
}

注意事项

多线程可以提高程序的处理能力,实现对共享资源的并发访问以及实现异步操作。在多线程编程中,常见的问题包括线程安全问题、死锁问题、上下文切换问题、数据同步问题和过度创建线程问题

1. 为了解决线程安全问题,可以使用同步机制(如synchronized关键字、Lock对象)、使用线程安全的数据结构或避免共享状态。

2. 为了解决死锁问题,需要避免循环等待资源、按照固定顺序获取资源或设置超时时间等。

3. 为了解决上下文切换问题,可以合理设计线程数量、减少线程间的竞争或使用线程池等。

4. 为了解决数据同步问题,可以使用锁来保证数据的原子性、使用volatile关键字保证可见性或使用线程安全的数据结构等。

5. 为了解决过度创建线程问题,可以使用线程池来复用线程、合理设置线程池大小等。

相关推荐

ES6中 Promise的使用场景?(es6promise用法例子)

一、介绍Promise,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大在以往我们如果处理多层异步操作,我们往往会像下面那样编写我们的代码doSomething(f...

JavaScript 对 Promise 并发的处理方法

Promise对象代表一个未来的值,它有三种状态:pending待定,这是Promise的初始状态,它可能成功,也可能失败,前途未卜fulfilled已完成,这是一种成功的状态,此时可以获取...

Promise的九大方法(promise的实例方法)

1、promise.resolv静态方法Promise.resolve(value)可以认为是newPromise方法的语法糖,比如Promise.resolve(42)可以认为是以下代码的语...

360前端一面~面试题解析(360前端开发面试题)

1.组件库按需加载怎么做的,具体打包配了什么-按需加载实现:借助打包工具(如Webpack的require.context或ES模块动态导入),在使用组件时才引入对应的代码。例如在V...

前端面试-Promise 的 finally 怎么实现的?如何在工作中使用?

Promise的finally方法是一个非常有用的工具,它无论Promise是成功(fulfilled)还是失败(rejected)都会执行,且不改变Promise的最终结果。它的实现原...

最简单手写Promise,30行代码理解Promise核心原理和发布订阅模式

看了全网手写Promise的,大部分对于新手还是比较难理解的,其中几个比较难的点:状态还未改变时通过发布订阅模式去收集事件实例化的时候通过调用构造函数里传出来的方法去修改类里面的状态,这个叫Re...

前端分享-Promise可以中途取消啦(promise可以取消吗)

传统Promise就像一台需要手动组装的设备,每次使用都要重新接线。而Promise.withResolvers的出现,相当于给开发者发了一个智能遥控器,可以随时随地控制异步操作。它解决了三大...

手写 Promise(手写输入法 中文)

前言都2020年了,Promise大家肯定都在用了,但是估计很多人对其原理还是一知半解,今天就让我们一起实现一个符合PromiseA+规范的Promise。附PromiseA+规范地址...

什么是 Promise.allSettled()!新手老手都要会?

Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的pr...

前端面试-关于Promise解析与高频面试题示范

Promise是啥,直接上图:Promise就是处理异步函数的API,它可以包裹一个异步函数,在异步函数完成时抛出完成状态,让代码结束远古时无限回掉的窘境。配合async/await语法糖,可...

宇宙厂:为什么前端离不开 Promise.withResolvers() ?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发。1.为什么需要Promise.with...

Promise 新增了一个超实用的 API!

在JavaScript的世界里,Promise一直是处理异步操作的神器。而现在,随着ES2025的发布,Promise又迎来了一个超实用的新成员——Promise.try()!这个新方法简...

一次搞懂 Promise 异步处理(promise 异步顺序执行)

PromisePromise就像这个词的表面意识一样,表示一种承诺、许诺,会在后面给出一个结果,成功或者失败。现在已经成为了主流的异步编程的操作方式,写进了标准里面。状态Promise有且仅有...

Promise 核心机制详解(promise机制的实现原理)

一、Promise的核心状态机Promise本质上是一个状态机,其行为由内部状态严格管控。每个Promise实例在创建时处于Pending(等待)状态,此时异步操作尚未完成。当异步操作成功...

javascript——Promise(js实现promise)

1.PromiseES6开始支持,Promise对象用于一个异步操作的最终完成(包括成功和失败)及结果值的表示。简单说就是处理异步请求的。之所以叫Promise,就是我承诺,如果成功则怎么处理,失败怎...

取消回复欢迎 发表评论: