线程池及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

相关文章

Linux线程同步/互斥过程详解

《Linux线程同步/互斥过程详解》文章讲解多线程并发访问导致竞态条件,需通过互斥锁、原子操作和条件变量实现线程安全与同步,分析死锁条件及避免方法,并介绍RAII封装技术提升资源管理效率... 目录01. 资源共享问题1.1 多线程并发访问1.2 临界区与临界资源1.3 锁的引入02. 多线程案例2.1 为

Python Web框架Flask、Streamlit、FastAPI示例详解

《PythonWeb框架Flask、Streamlit、FastAPI示例详解》本文对比分析了Flask、Streamlit和FastAPI三大PythonWeb框架:Flask轻量灵活适合传统应用... 目录概述Flask详解Flask简介安装和基础配置核心概念路由和视图模板系统数据库集成实际示例Stre

Olingo分析和实践之OData框架核心组件初始化(关键步骤)

《Olingo分析和实践之OData框架核心组件初始化(关键步骤)》ODataSpringBootService通过初始化OData实例和服务元数据,构建框架核心能力与数据模型结构,实现序列化、URI... 目录概述第一步:OData实例创建1.1 OData.newInstance() 详细分析1.1.1

Java中的xxl-job调度器线程池工作机制

《Java中的xxl-job调度器线程池工作机制》xxl-job通过快慢线程池分离短时与长时任务,动态降级超时任务至慢池,结合异步触发和资源隔离机制,提升高频调度的性能与稳定性,支撑高并发场景下的可靠... 目录⚙️ 一、调度器线程池的核心设计 二、线程池的工作流程 三、线程池配置参数与优化 四、总结:线程

WinForm跨线程访问UI及UI卡死的解决方案

《WinForm跨线程访问UI及UI卡死的解决方案》在WinForm开发过程中,跨线程访问UI控件和界面卡死是常见的技术难题,由于Windows窗体应用程序的UI控件默认只能在主线程(UI线程)上操作... 目录前言正文案例1:直接线程操作(无UI访问)案例2:BeginInvoke访问UI(错误用法)案例

Linux线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

SpringBoot线程池配置使用示例详解

《SpringBoot线程池配置使用示例详解》SpringBoot集成@Async注解,支持线程池参数配置(核心数、队列容量、拒绝策略等)及生命周期管理,结合监控与任务装饰器,提升异步处理效率与系统... 目录一、核心特性二、添加依赖三、参数详解四、配置线程池五、应用实践代码说明拒绝策略(Rejected

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操