首页 > 其他分享 >线程池详解

线程池详解

时间:2023-11-27 17:23:38浏览次数:27  
标签:队列 阻塞 任务 详解 线程 workQueue CPU

1. 线程池

作用

  • 提升资源使用率,避免无意义的线程重复创建销毁成本

  • 提升反应速度,已提前创建线程

  • 方便管理线程资源,如可控制并发量、批量中断等

参数

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
		...
	}
  • corePoolSize
    核心线程数最大值。
    添加任务时,只要当前核心线程数小于此值,都会新建线程来执行。并且,在任务结束后超过keepAliveTime时间也不会被回收。
    核心线程从空的阻塞队列取任务时,会走workQueue.take(),基本调用Condition实例notEmpty.await()方法持续阻塞;非核心线程则走 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS),基本调用Condition实例notEmpty.awaitNanos(nanos)进行超时等待,若超过keepAliveTime后还无任务,便会返回null结束并被回收。

    Runnable r = timed ?
       workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
       workQueue.take();
    
  • maximumPoolSize
    最大线程数。当线程池当前所有线程都在执行任务,且等待队列已满时,会持续根据需要创建新的线程,并到此线程数为止。

  • keepAliveTime
    核心线程如果处于闲置状态超过该值,就会被销毁。如果设置allowCoreThreadTimeOut(true),则会也作用于核心线程。

  • unit
    时间单位,可设置纳秒、微秒、毫秒、秒、分、时、天。

  • workQueue
    阻塞线程,用于缓存任务。

    阻塞线程 类型 特点
    ArrayBlockingQueue 有界阻塞队列 数组实现,需要指定队列大小,支持公平锁和非公平锁
    LinkedBlockingQueue 无界阻塞队列 链表实现,默认容量Interge.MAX_VALUE,也可以指定大小。也是newFixedThreadPool()和newSingleThreadExecutor()的等待队列。
    LinkedBlockingDeque 有界阻塞队列 链表实现,具有双向存取功能。在高并发下,相比于LinkedBlockingQueue可以将锁竞争降低最多一半。
    PriorityBlockingQueue 无界阻塞队列 优先级排序,默认使用自然排序
    SynchronousQueue 无界阻塞队列 无缓冲的阻塞队列,没有任何内部容量,甚至连一个队列的容量都没有。
    公平模式底层使用的是先进先出的队列TransferQueue,非公平模式底层采用了后进先出的TransferStack栈来实现。
    LinkedTransferQueue 无界阻塞队列 链表实现
    DelayQueue 无界延时队列 延迟队列,该队列中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素 。底层数据是数组实现的堆结构。
  • threadFactory
    创建线程的工厂。ThreadFactory类是一个接口,只有newThread()方法。如果需要自定义线程名称格式,可以自定义类实现此接口。

    public interface ThreadFactory {
      Thread newThread(Runnable r);
    }
    
  • handler
    拒绝处理策略。
    如果核心线程数、等待队列、最大线程数都满了,那么会采取拒绝策略进行处理。拒绝策略的调用是在主线程处理的。
    默认的四种策略,均是ThreadPoolExecutor类中公共静态类:

    • AbortPolicy
      默认拒绝处理策略,丢弃任务并抛出RejectedExecutionException异常。

    • DiscardPolicy
      丢弃新来的任务,但是不抛出异常

    • DiscardOldestPolicy
      丢弃等待队列头部(最旧的)的任务,然后重新尝试执行程序,将任务添加到队列中(如果再次失败,重复此过程)

    • CallerRunsPolicy
      由调用线程处理该任务。

    也可以自定义拒绝处理策略,实现RejectedExecutionHandler接口并重写rejectedExecution(Runnable r, ThreadPoolExecutor executor)方法即可。

    public class RejectedExecutionHandlerImpl implements RejectedExecutionHandler {
      @Override
      public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
          System.out.println(r.toString() + " is rejected");
      }
    }
    

执行过程

待补充。

不建议使用Executors创建线程池

方法 特点
newFixedThreadPool 线程数固定,阻塞队列为无界队列LinkedBlockingQueue,队列过大可能内存溢出
newCachedThreadPool 核心线程数为0,阻塞队列为SynchronousQueue,但是最大线程数为Integer.MAX_VALUE,非核心线程数过多可能内存溢出
newScheduledThreadPool 阻塞队列为DelayedWorkQueue,但是最大线程数为Integer.MAX_VALUE,线程过多可能内存溢出
newSingleThreadExecutor 线程数为1,阻塞队列为无界队列LinkedBlockingQueue,队列过大可能内存溢出

2. 线程数多少合适

使用多线程的目的,是为了提升性能,落地到具体指标:

  • 吞吐量
    单位时间内能处理的请求数量

  • 延迟
    从发出请求到收到响应的时间

  • 并发量
    能同时处理的请求数量

一般随着并发量的增加,延迟也会增加。所以延迟这个指标,一般都会是基于并发量来说的。

CPU密集型

最佳线程数 = CPU逻辑核心数 + 1

当线程因为偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程(+1)可以顶上,从而保证 CPU 的利用率。

IO密集型

最佳线程数 = 1 +(I/O 耗时 / CPU 耗时)

当线程 A 执I/O 耗时 / CPU 耗时行 IO 操作时,另外N个线程正好执行完各自的 CPU 计算。这样 CPU 的利用率就达到了 100%。但是实际情况很难评估具体的I/O耗时和CPU耗时,需要实际测试调整。

动态化线程池

针对涉及IO等阻塞操作的模型来说,评估实际的IO或者CPU耗时不太现实,故若是可以监控到相应指标,便可以利用ThreadPoolExecutor设置核心线程数、最大线程数等实例方法进行动态调整。
针对IO密集型任务,可以根据活跃线程数/最大线程数的比例或者RejectedExecut异常次数的阈值进行判断,适当增加动态线程数及最大线程数。
针对高吞吐量任务,可以根据等待队列中的任务数量/等待队列的长度的比例或者RejectedExecut异常次数的阈值进行判断,适当增加动态线程数及最大线程数。

标签:队列,阻塞,任务,详解,线程,workQueue,CPU
From: https://www.cnblogs.com/kiper/p/17858718.html

相关文章

  • 详解如何使用VSCode搭建TypeScript环境(适合小白)
     搭建Javascript环境因为TypeScript不能直接在浏览器上运行。它需要编译器来编译并生成JavaScript文件。所以需要首先安装好javascript环境,可以参考文章:https://blog.51cto.com/liwen629/7621120全局安装Typescript模块执行下面命令进行安装npminstall-gtypescript安装完成后我......
  • Idea中使用Debug模式​详解
    Idea中使用Debug模式Debug用来追踪代码的运行流程,通常在程序运行过程中出现异常,启用Debug模式可以分析定位异常发生的位置,以及在运行过程中参数的变化。通常我们也可以启用Debug模式来跟踪代码的运行流程去学习三方框架的源码。Debug开篇首先看下IDEA中Debug模式下的界面。如下是在......
  • 【分享】GPS北斗卫星对时服务(NTP时间服务器)搭建教程详解
    【分享】GPS北斗卫星对时服务(NTP时间服务器)搭建教程详解【分享】GPS北斗卫星对时服务(NTP时间服务器)搭建教程详解京准电子科技官微——ahjzsz网络时间协议,英文名称:Network Time Protocol(NTP)是用来使计算机时间同步化的一种协议,这篇文章主要介绍了Windows搭建NTP时间同步服务器......
  • socket测试(多线程,课上测试)
    基于socket实现daytime(13)服务器(端口我们使用13+后三位学号)和客户端服务器响应消息格式是“客户端IP:XXXX客户端PID:XXXX服务器tid:XXXX服务器实现者学号:XXXXXXXX当前时间:XX:XX:XX”注意服务器端要通过多线程实现,每次客户端链接到服务器,服务器就启动一个新线程和客户端连......
  • python threading线程数
    importthreadingimporttimename_list=[{"李四1":1234556},{"李四2":1234556},{"李四3":1234556},{"李四4":1234556},{"李四5":1234556},{"李四6":1234556},{"李四7"......
  • C++ Windows版本线程池
    使用:ThreadPoolthreadPool(12);//设定数量threadPool.queue(myFunction,args1,args2,...);//创建任务实现:#include<windows.h>#include<iostream>#include<functional>#include<vector>#include<queue>#include<mutex>#include......
  • MyBatis `<include refid="XXX">`标签详解
    MyBatis<includerefid="XXX">标签详解MyBatis作为一种优秀的持久化框架,提供了丰富的XML配置选项,其中<include>标签是一个非常有用的特性,用于引入SQL片段,提高代码的可维护性和可读性。解释<include>标签用于引用SQL代码片段。refid是引用的SQL片段的id名称,必须保持唯一。......
  • 多线程复制文件夹
    pThread复制文件夹C++通过pthread复制文件夹。主要处理普通文件和链接文件。事实上只要处理好链接文件即可。我们可以通过判断链接的文件是文件夹还是文件即可判断对当前文件是通过文件直接复制还是通过文件夹递归复制。#include<iostream>#include<dirent.h>#include<stri......
  • std::future与std::promise在C++多线程同步与数据共享中的应用
    1、std::promise与std::futurestd::promise与std::future通过配合使用完成数据的同步与共享,两者均是模板类;std::promise存储异步执行的值或异常;std::future提供可供访问的异步执行结果。二者配合使用伪码如下:std::promise<Type>pr;std::future<Type>fu(pr.get_fu......
  • Spring MVC学习随笔-控制器(Controller)开发详解:接受客户端(Client)请求参数
    学习视频:孙哥说SpringMVC:结合Thymeleaf,重塑你的MVC世界!|前所未有的Web开发探索之旅第三章、SpringMVC控制器开发详解3.1核心要点......