线程池及Executor框架

2023-12-27 05:48
文章标签 线程 框架 executor 池及

本文主要是介绍线程池及Executor框架,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

为什么要使用线程池?

    诸如web服务器、数据库服务器、文件服务器或邮件服务之类的许多服务器应用程序都面向处理来自远程的大量短小的任务。请求以某种方式到达服务器,这种方式可以通过网络协议(HTTP、FTP)通过JMS队列或者可能通过轮询数据库。不管请求如何到达,服务器应用程序经常出现的情况是:单个任务处理的时间很短而请求的数目却巨大。如果每一个请求到达就创建一个新的线程,然后在新线程中处理请求,这样频繁的创建线程、销毁线程对系统的开销是非常大的。

    线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到多个任务上。优点是,1、在请求到达时线程已经存在,所以无意中消除了线程创建带来的时间延迟。这样就可以立即处理请求,减少应用的响应时间。2、通过适当的调整线程池中的线程数目,也就是当请求的数目超过某个阀值时,就强制其他任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。

风险与机遇

    用线程池构建应用程序容易遭受任何其他多线程应用程序容易遭受的并发风险,诸如同步错误和死锁,还容易遭受特定于线程池的少量风险,诸如与池有关的死锁、资源不足和线程泄漏。

Future与Callable、FutureTask

    Callable与Runnable功能相似,Callable的call有返回值,可以返回给客户端,而Runable没有返回值,一般情况下,Callable与FutureTask一起使用,或者通过线程池的submit方法返回相应的Future。

   Future就是对具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果操作。get方法阻塞,直到任务返回结果。

  FutureTask则是一个RunnableFuture,而RunnableFuture实现了Runnable又实现了Future这两个接口。

线程池的核心组成部分及其运行机制

    corePoolSize:核心线程池大小 cSize

    maximumPoolSize:线程池最大容量 mSize

    keepAliveTime:当线程数量大于核心时,多余的空闲线程在终止之前等待新任务的最大时间。

    unit:时间单位

    workQueue:工作队列 nworks

    ThreadFactory:线程工厂

    handler:拒绝策略

运行机制

    通过new创建线程池时,除非调用prestartAllCoreThread方法初始化核心线程,否则此时线程池中有0个线程,即时工作队列中存在多个任务,同样不会执行。

    任务数X

    x<=cSize 只启动x个线程

    x>=cSize && x<nWork +cSize  会启动<=cSize 个线程 其他任务就放在工作队列里

   当  x > cSize && x > nWork+cSize 时

        x-(nworks) <= mSize 会启动x-(nworks) 个线程

        x-(nworks) > mSize 会启动mSize个线程来执行任务,其余的执行相应的拒绝策略    

线程池拒绝策略

   AbortPolicy:该策略直接抛出异常,阻止系统正常工作。

   CallerRunsPolicy:只要线程没有关闭,该策略直接在调用线程执行当前被丢弃的任务。

   DiscardPolicy:什么事都不做,直接把任务丢弃。

   DiscardOldestPolicy:丢弃最老的一个请求(任务队列里面的第一个)

 

import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class ThreadPoolExecutorDemo {public static void main(String[] args) {LinkedBlockingQueue<Runnable> linkedBlockingQueue = new LinkedBlockingQueue<>(20);ThreadPoolExecutor threadPoolExecutor  = new ThreadPoolExecutor(10,20,3L, TimeUnit.SECONDS,linkedBlockingQueue);Future<?> future = null;for (int i = 0;i<40;i++){threadPoolExecutor.submit(()->{try {Thread.sleep(2000L);}catch (InterruptedException e){e.printStackTrace();}});System.out.println(threadPoolExecutor.getActiveCount());}threadPoolExecutor.prestartAllCoreThreads();}
}

运行结果

通过修改代码中的初始化参数可以验证上面的理论。

Executor框架

   通过相应的方法,能创建出6种线程池。

ExecutorService executorService = Executors.newCachedThreadPool();

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);

ExecutorService workStealingPool = Executors.newWorkStealingPool();

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

ScheduledExecutorService singleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();

newCachedThreadPool:创建一个可以根据需要 创建新线程的线程池,如有空闲线程,优先使用空闲的线程。

newFixedThreadPool:创建一个固定大小的线程池,在任何时候,最多只有N个线程在处理任务

newScheduledThreadPool:能延迟执行、定时执行的线程池。

newWorkStealingPool:工作窃取,使用多个队列来减少竞争。

newSingleThreadExecutor:单一线程的线程池,只是用唯一一个线程来执行任务,即时提交再多的任务,也都是会放在等待队列。

线程池的使用建议

   尽量避免使用Executor框架创建线程池

      newFixedThreadPool newSingleThreadExecutor 

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

      newCachedThreadPool newScheduledThreadPool

      允许的创建线程数量为 Integer.MAX_VALUE,可能就会创建大量的线程,从而导致OOM

      为什么第二个例子,在限定的堆的内存之后,还会把整个电脑的内存撑爆

            创建线程池时,核心线程数不要过大

            相应的逻辑,发现异常时要时常处理

            submit 如果发生异常,不会立即抛出,而是在get的时候,再抛异常。

            execute 直接抛出异常。

 

 

 

 

 

 

 

 

 

 

这篇关于线程池及Executor框架的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

SpringBoot集成图片验证码框架easy-captcha的详细过程

《SpringBoot集成图片验证码框架easy-captcha的详细过程》本文介绍了如何将Easy-Captcha框架集成到SpringBoot项目中,实现图片验证码功能,Easy-Captcha是... 目录SpringBoot集成图片验证码框架easy-captcha一、引言二、依赖三、代码1. Ea

Gin框架中的GET和POST表单处理的实现

《Gin框架中的GET和POST表单处理的实现》Gin框架提供了简单而强大的机制来处理GET和POST表单提交的数据,通过c.Query、c.PostForm、c.Bind和c.Request.For... 目录一、GET表单处理二、POST表单处理1. 使用c.PostForm获取表单字段:2. 绑定到结

Spring Boot 中正确地在异步线程中使用 HttpServletRequest的方法

《SpringBoot中正确地在异步线程中使用HttpServletRequest的方法》文章讨论了在SpringBoot中如何在异步线程中正确使用HttpServletRequest的问题,... 目录前言一、问题的来源:为什么异步线程中无法访问 HttpServletRequest?1. 请求上下文与线

在 Spring Boot 中使用异步线程时的 HttpServletRequest 复用问题记录

《在SpringBoot中使用异步线程时的HttpServletRequest复用问题记录》文章讨论了在SpringBoot中使用异步线程时,由于HttpServletRequest复用导致... 目录一、问题描述:异步线程操作导致请求复用时 Cookie 解析失败1. 场景背景2. 问题根源二、问题详细分

Java多线程父线程向子线程传值问题及解决

《Java多线程父线程向子线程传值问题及解决》文章总结了5种解决父子之间数据传递困扰的解决方案,包括ThreadLocal+TaskDecorator、UserUtils、CustomTaskDeco... 目录1 背景2 ThreadLocal+TaskDecorator3 RequestContextH

java父子线程之间实现共享传递数据

《java父子线程之间实现共享传递数据》本文介绍了Java中父子线程间共享传递数据的几种方法,包括ThreadLocal变量、并发集合和内存队列或消息队列,并提醒注意并发安全问题... 目录通过 ThreadLocal 变量共享数据通过并发集合共享数据通过内存队列或消息队列共享数据注意并发安全问题总结在 J

修改若依框架Token的过期时间问题

《修改若依框架Token的过期时间问题》本文介绍了如何修改若依框架中Token的过期时间,通过修改`application.yml`文件中的配置来实现,默认单位为分钟,希望此经验对大家有所帮助,也欢迎... 目录修改若依框架Token的过期时间修改Token的过期时间关闭Token的过期时js间总结修改若依

异步线程traceId如何实现传递

《异步线程traceId如何实现传递》文章介绍了如何在异步请求中传递traceId,通过重写ThreadPoolTaskExecutor的方法和实现TaskDecorator接口来增强线程池,确保异步... 目录前言重写ThreadPoolTaskExecutor中方法线程池增强总结前言在日常问题排查中,

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

C语言线程池的常见实现方式详解

《C语言线程池的常见实现方式详解》本文介绍了如何使用C语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧... 目录1. 线程池的基本结构2. 线程池的实现步骤3. 线程池的核心数据结构4. 线程池的详细实现4.1 初