• 多线程之线程池


    1.为什么使用线程池

    通俗的讲,线程池就是一个线程集合,里面已经提前创建好了若干个线程,当需要线程的时候到线程集合里获取一个即可,这样省去了创建线程的时间,当然也省去了GC回收线程的时间,当线程池里的线程都被使用了后,只能阻塞等待了,等待获取线程池后被释放的线程,这样就不会无限制的去创建线程而导致Out of Memory

    2.线程池使用

    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class Test {
        public static void main(String[] args) {
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 3L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20));
            for (int i = 0; i < 20; i++) {
                threadPoolExecutor.submit(() -> {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName());
                });
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3.ThreadPoolExecutor构造函数参数作用

    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue workQueue,
                                  ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler)
                               
    corePoolSize:核心线程数,线程池会维护线程的最少数量,默认情况下核心线程会一直存活,即使没有任务也不会受keepAliveTime控制
    	坑:在刚创建线程池时线程不会立刻启动,到有任务提交时才开始创建线程并逐步线程数目达到corePoolSize
    	
    maximumPoolSize:线程池维护线程的最大数量,超过将被阻塞
    	坑:当核心线程满,且阻塞队列也满时,才会判断当前线程数是否小于最大线程数,才决定是否创建新线程
    	
    keepAliveTime:非核心线程的闲置超时时间,超过这个时间就会被回收,直到线程数量等于corePoolSize
    
    unit:指定keepAliveTime的单位,如TimeUnit.SECONDS、TimeUnit.MILLISECONDS
    
    workQueue:线程池中的任务队列,常用的是ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue
    
    threadFactory:创建新线程时使用的工厂
    
    handler:RejectedExecutionHandler是一个接口且只有一个方法,线程池中的数量大于maximumPoolSize,对拒绝任务的处理策略,默认有4种策略:AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 运行机制,线程数个数,假设x是任务数
      • x <= 核心线程数,只启动x个线程
      • x > 核心线程数 && x < 任务队列 + 核心线程数,会启动核心线程数,其他的任务放到任务队列里
      • x > 核心线程数 && x > 任务队列 + 核心线程数
        • x - 任务队列 <= 最大数量,会启动(x - 任务队列)个线程
        • x - 任务队列 > 最大数量,会启动最大数量个线程来执行任务,其余任务执行相应的拒绝策略
    • 线程池拒绝策略
      • AbortPolicy:该策略直接抛出异常,阻止系统正常工作
      • CallerRunsPolicy:只要线程池没有关闭,该策略直接在调用者线程中执行当前被丢弃的任务
      • DiscardPolicy:直接丢弃任务
      • DiscardOldestPolicy:丢弃最老的一个请求(任务队列的第一个),再尝试提交任务

    4.线程池使用建议

    • 尽量避免使用Executors创建线程池

      Executors创建线程池底层也是调用ThreadPoolExecutor,只不过使用不同的参数、队列、拒绝策略等,如果使用不当,会造成资源耗尽问题;直接使用ThreadPoolExecutor让使用者更加清楚线程池规则、常见参数的使用,避免风险

      常见的线程池问题:

      newFixedThreadPool和newSingleThreadExecutor:队列使用LinkedBlockingQueue,队列长度为Integer.MAX_VALUE,可能造成堆积导致OOM
      newScheduledThreadPool和newCachedThreadPool:线程池里面允许最大的线程数是Integer.MAX_VALUE,可能会创建多线程导致OOM

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

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

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

    • execute发生异常直接抛出异常

  • 相关阅读:
    Leetcode236 二叉树两节点的最近公共祖先
    机器人中的数值优化(十五)——PHR增广拉格朗日乘子法
    困难的实训项目(关键词-解决方案)(相关搜索:虚拟机|操作系统)(相关搜索:虚拟机|操作系统)
    Scala第十九章节
    【二】spring boot-设计思想
    1.4_27 Axure RP 9 for mac 高保真原型图 - 案例26【中继器 - 后台管理系统4】功能-删除数据
    从模型到部署,教你如何用Python构建机器学习API服务
    .NET Emit 入门教程:第七部分:实战项目1:将 DbDataReader 转实体
    PCL 添加自定义数据类型
    RK3588 I2C设备开发硬件检测
  • 原文地址:https://blog.csdn.net/2302_76363587/article/details/130903569