当前位置:   article > 正文

Java并发编程:第四章 线程池(Executor框架)_java executor

java executor

一、为什么需要线程池

1、线程的创建

Java线程的创建非常昂贵,需要JVM和OS(操作系统)配合完成大量的工作:

  • 必须为线程堆栈分配和初始化大量内存块,其中包括至少1MB的栈内存
  • 需要进行系统调用,以便在OS(操作系统)中创建和注册本地线程

2、线程销毁

Java高并发应用频繁创建和销毁线程的操作是非常低效的,而且是不被编程规范锁允许的。在Java中,线程的销毁(即线程结束其执行并释放资源)本身并不是一个效率特别低下的操作,但它可能涉及一些开销,尤其是在大量线程频繁创建和销毁的场景中。以下是一些影响线程销毁效率的因素:

  • 资源释放:当线程结束时,它需要释放其占用的系统资源,如栈内存、线程ID、线程状态等。这些资源的释放操作需要一定的时间。
  • 并发安全性:如果在多线程环境下进行线程销毁,还需要考虑线程的并发安全性,避免出现并发访问共享资源的问题,这可能需要引入同步机制,增加了复杂性和性能开销。
  • 垃圾回收:线程对象和其他与之关联的对象在不再被引用时,会被Java的垃圾回收器标记为可回收。垃圾回收过程本身可能会产生一些开销,尤其是在有大量对象需要回收时。
  • 上下文切换:如果线程的销毁导致操作系统的上下文切换(例如,从用户态切换到内核态进行资源清理),那么这也可能产生一定的开销。

二、优点

1、降低资源消耗

通过重复利用已经创建的线程降低线程创建和销毁造成的消耗。

2、提高响速度

当任务到达时,任务可以不需要等到线程创建就能立即执行。

3、线程管理

进行统一分配、调优和监控。

三、架构说明

Java中线程池是通过Executor框架实现,该框架中用到了Executor、Executors(代表工具类)、ExecutorService、ThreadPoolExecutor这几个类在这里插入图片描述

四、Executors工具类

1、介绍

Executors 是 Java 并发包 java.util.concurrent 中的一个工具类,它提供了一组用于创建线程池的方法。线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的 ThreadFactory 创建一个新线程。

Executors 工具类的主要目的是简化线程池的创建和管理。通过它,你可以很容易地创建固定大小的线程池、可缓存的线程池、定时线程池等,而无需手动处理 ThreadPoolExecutor 的复杂配置。

2、Executors工具类常用的方法

(1)newFixedThreadPool(int nThreads):

创建一个固定大小的线程池。如果所有线程都在工作,那么新提交的任务会在一个队列中等待,直到有线程可用。

new ThreadPoolExecutor(var0, var0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
  • 1

(2) newCachedThreadPool()

创建一个不限制线程数量的线程池,任何提交的任务都将立即执行,但是空闲线程会得到及时回收。

new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>())
  • 1
  • 2
  • 3

(3)newSingleThreadExecutor()

创建一个单线程的线程池。这意味着在任何时候,都只有一个任务在执行。

new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>())
  • 1
  • 2
  • 3

(4)newScheduledThreadPool(int corePoolSize):

创建一个定时线程池,它可以在给定的延迟后运行命令,或者定期地执行命令。

五、ThreadPoolExecutor

1、构造函数

	ThreadPoolExecutor(int corePoolSize,   //核心线程数,即使线程空闲也不回收
                              int maximumPoolSize,   //线程数的上限
                              long keepAliveTime,   //线程最大空闲时长
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,   //任务的排队队列
                              ThreadFactory threadFactory,   //新线程的产生方式
                              RejectedExecutionHandler handler)   //拒绝策略
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2、参数含义

  • corePoolSize:线程池中核心线程的数量

  • keepAliveTime:线程空闲的时间

  • unit:keepAliveTime的单位

  • workQueue:用来保存等待执行的任务的阻塞队列,常见的阻塞队列:

    • ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序。
    • LInkedBlockingQueue:是 一个基于链表接口的阻塞队列,此队列按FIFO排序元素
    • SynchronousQueue:是一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态
    • PriorityBlockingQueue:一个具有优先级的无限阻塞队列
  • threadFactory:用于设置创建线程的工厂,默认是DefaultThreadFactory

  • handler:RejectedExecutionHandler, 线程池的拒绝策略,分类:

    • AbortPolicy:直接抛出异常,默认策略
    • CallerRunsPolicy:用调用者所在的线程来执行任务
    • DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务
    • DiscardPolicy:不处理,丢弃掉
    • 自定义策略:根据应用场景需要实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化存储不能处理的任务

3、线程池的处理流程

在这里插入图片描述

4、线程池的监控

如果在系统中大量使用线程池,则有必要对线程池进行监控,方便在出现问题时,可以根据线程池的使用情况快速定位问题。线程池的监控是确保线程池稳定性和性能的关键环节。以下是一些建议的监控方法和工具:

  • 使用监控工具:可以使用一些专门的监控工具,如VisualVM、Grafana、Prometheus等,这些工具能够实时监控线程池的运行情况,并绘制相应的图表和指标。通过这些工具,可以获取线程池的状态信息,如线程数量、活跃线程数、任务队列长度等,从而及时发现并解决潜在问题。
  • 继承线程池并重写方法:通过继承线程池并重写其部分方法,可以在任务执行前、执行后和线程池关闭前进行自定义操作。例如,可以重写beforeExecute、afterExecute和terminated方法,以监控任务的平均执行时间、最大执行时间和最小执行时间等关键指标。
  • 使用线程池的参数进行监控:线程池中有一些关键参数,通过监控这些参数,可以了解线程池的工作负载、任务完成情况和资源利用情况。线程池的监控参数主要包括以下几个方面:
    • 任务执行情况指标:
      • taskCount:线程池需要执行的任务数量。
      • completedTaskCount:线程池已完成的任务数量。
      • getActiveCount():线程池中正在执行任务的线程数量。
      • getCompletedTaskCount():线程池已完成的任务数量。
    • 线程使用情况指标:
      • poolSize或 getPoolSize():线程池当前的线程数量。
      • getCorePoolSize():线程池的核心线程数量。
      • getLargestPoolSize():线程池曾经创建过的最大线程数量。
      • queueSize:还剩多少个任务未执行。
    • 异常情况指标:
      • taskRejectedCount:任务拒绝次数。
      • taskTimeoutCount:任务超时次数。
  • 自定义监控指标:除了利用线程池自带的参数和工具,还可以根据具体需求自定义监控指标。例如,可以监控线程池的队列饱和度、单位时间内提交任务的速度与消费速度的比值等,以便更全面地了解线程池的性能状况。

综上所述,线程池的监控是一个综合性的任务,需要结合多种方法和工具来实现。通过有效的监控,可以及时发现并解决线程池中的问题,从而确保系统的稳定性和性能。

5、线程池优雅关闭:(一般情况下,线程池启动后建议手动关闭)

(1)线程池的5中状态:

  • RUNNING:线程池创建之后的初始化状态,这种状态下可以执行任务
  • SHUTDOWN:该状态下线程池不再接受新任务,但是会将工作队列中的任务执行完毕
  • STOP:该状态下线程池不再接受新任务,也不会处理工作队列中的剩余任务,并且将会中断所有工作线程
  • TIDYING:该状态下所有任务都已终止或者处理完成,将会执行terminated()钩子方法
  • TERMINATED:执行完terminated()钩子方法之后的状态

(2)线程池状态转换规则

  • 线程池创建之后状态为RUNNING。
  • 执行线程池的shutdown()实例方法,会使线程池状态从RUNNING转变为SHUTDOWN
  • 执行线程池shutdownNow()实例方法,会使线程池状态从RUNNING转变为STOP
  • 当线程池处于SHUTDOWN状态时,执行其shutdownNow()方法会将其状态转变为STOP
  • 等待线程池的所有工作线程停止,工作队列清空之后,线程池状态会从STOP转变为TIDYING
  • 执行完terminated()钩子方法之后,线程池状态从TIDYING转变为TERMINATED

(3)关闭线程池方法

  • shutdown:线程池的状态设置成SHUTDOWN状态,拒绝新任务的提交,并等待所有任务有序地执行完毕,中断没有正在执行任务的线程
  • shutdownNow:将线程池的状态设置为STOP,然后尝试停止所有的正在执行或者暂停任务的线程
本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/在线问答5/article/detail/926023
推荐阅读
相关标签
  

闽ICP备14008679号