0
点赞
收藏
分享

微信扫一扫

STM32F103驱动oled显示屏

书坊尚 2023-08-30 阅读 50

线程池

Java 线程池是一种多线程处理技术,它可以在程序中预先创建一定数量的线程,将任务提交到线程池中,线程池会自动调度线程执行任务。通过使用线程池,可以避免反复创建和销毁线程的开销,提高程序性能,同时也能够避免线程资源的浪费和过度使用。

为什么使用线程池?

在并发环境下,系统不能够确定在任意时刻中,有多少任务需要执行,有多少资源需要投入。这种不确定性将带来以下若干问题:
1、频繁申请/销毁资源和调度资源,将带来额外的消耗,可能会非常巨大。
2、对资源无限申请缺少抑制手段,易引发系统资源耗尽的风险。
3、系统无法合理管理内部的资源分布,会降低系统的稳定性。

Java 线程池的主要优势包括:
1、重用线程:线程池中的线程可以被重复利用,避免了创建和销毁线程的开销,提高了程序的性能。
2、提高程序的响应速度:当有任务到来时,线程池中已经有了准备好的线程,可以立即处理任务,而不需要等待线程的创建。
3、提高资源利用率:由于线程的创建和销毁是一项昂贵的操作,使用线程池可以避免不必要的资源浪费。

Java线程池涉及内容

java中涉及到线程池的相关类均在jdk1.5开始的java.util.concurrent包中,涉及到的几个核心类及接口包括:Executor、Executors、ExecutorService、ThreadPoolExecutor、FutureTask、Callable、Runnable等。
这些接口和类都是Java中用于实现线程池的核心组件,它们的作用如下:
1、Executor 接口定义了一种将任务提交与任务执行分离的标准方式,它只有一个方法 execute(Runnable command),用于执行传入的任务。
2、Executors 类是一个工厂类,提供了创建各种类型线程池的静态工厂方法。例如,newFixedThreadPool(int nThreads) 方法可以创建一个固定大小的线程池。
3、ExecutorService 接口继承自 Executor 接口,提供了更加丰富的任务执行管理方法,例如可以通过 submit(Callable task) 方法提交一个有返回值的任务,并返回一个 Future 对象来表示任务的执行结果。
4、ThreadPoolExecutor 类是一个线程池的具体实现,实现了 ExecutorService 接口。它可以根据需要自动调整线程池中的线程数量,以满足当前任务的需求。
5、FutureTask 类表示一个异步计算任务,它实现了 RunnableFuture 接口,可以在执行完成后返回一个结果。它可以被提交给 Executor 或 ExecutorService 进行执行。
6、Callable 接口类似于 Runnable 接口,但它可以返回一个值,并且可以抛出异常。
7、Runnable 接口表示一个可以被执行的任务。

Java线程池通常由以下四个组件组成:
任务队列:用于存放需要执行的任务。
线程池管理器:用于创建、销毁线程池,并控制线程池的状态。
工作线程:实际执行任务的线程。
任务:需要执行的具体业务逻辑。

线程池的创建及重要参数

Java 线程池的实现主要通过 Executor 框架提供的线程池接口实现。常用的线程池类型包括:
1、FixedThreadPool:固定大小的线程池,可以控制并发线程的数量。
2、CachedThreadPool:可以根据需要创建新的线程,但在可用线程的数量超过处理需求时,线程池会回收空闲的线程。
3、ScheduledThreadPool:用于执行周期性任务和延迟任务,支持定时执行任务和循环执行任务。
4、SingleThreadExecutor:只有一个工作线程的线程池,可以保证所有的任务按照指定的顺序在一个线程中执行。

  public static ExecutorService newFixedThreadPool(int var0) {
        return new ThreadPoolExecutor(var0, var0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
  }
	
  public static ExecutorService newSingleThreadExecutor() {
        return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));
  }
 
  public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());
  }
 
  public static ScheduledExecutorService newScheduledThreadPool(int var0) {
        return new ScheduledThreadPoolExecutor(var0);
  }

Java中的线程池核心实现类是ThreadPoolExecutor。
在这里插入图片描述

 public ThreadPoolExecutor(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<Runnable> workQueue,
                           ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {……}

使用线程池的关键是合理地配置线程池的参数,例如线程池的大小、任务队列的大小、线程的超时时间等。如果线程池的参数设置不当,可能会导致线程池中的线程数量不足或者过多,从而影响程序的性能。

ThreadPoolExecutor实现的顶层接口是Executor,顶层接口Executor提供了一种思想:将任务提交和任务执行进行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供Runnable对象,将任务的运行逻辑提交到执行器(Executor)中,由Executor框架完成线程的调配和任务的执行部分。ExecutorService接口增加了一些能力:(1)扩充执行任务的能力,补充可以为一个或一批异步任务生成Future的方法;(2)提供了管控线程池的方法,比如停止线程池的运行。AbstractExecutorService则是上层的抽象类,将执行任务的流程串联了起来,保证下层的实现只需关注一个执行任务的方法即可。最下层的实现类ThreadPoolExecutor实现最复杂的运行部分,ThreadPoolExecutor将会一方面维护自身的生命周期,另一方面同时管理线程和任务,使两者良好的结合从而执行并行任务。

ThreadPoolExecutor是如何运行,如何同时维护线程和执行任务的呢?其运行机制如下图所示:

在这里插入图片描述

corePoolSize:核心线程数,也是线程池中常驻的线程数,线程池初始化时默认是没有线程的,当任务来临时才开始创建线程去执行任务
maximumPoolSize:最大线程数,在核心线程数的基础上可能会额外增加一些非核心线程,需要注意的是只有当workQueue队列填满时才会创建多于corePoolSize的线程(线程池总线程数不超过maxPoolSize)
keepAliveTime:非核心线程的空闲时间超过keepAliveTime就会被自动终止回收掉,注意当corePoolSize=maxPoolSize时,keepAliveTime参数也就不起作用了(因为不存在非核心线程);
unit:keepAliveTime的时间单位
workQueue:用于保存任务的队列,可以为无界、有界、同步移交三种队列类型之一,当池子里的工作线程数大于corePoolSize时,这时新进来的任务会被放到队列中
threadFactory:创建线程的工厂类,默认使用Executors.defaultThreadFactory(),也可以使用guava库的ThreadFactoryBuilder来创建
handler:线程池无法继续接收任务(队列已满且线程数达到maximunPoolSize)时的饱和策略,取值有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy

workQueue队列

SynchronousQueue(同步移交队列):队列不作为任务的缓冲方式,可以简单理解为队列长度为零
LinkedBlockingQueue(无界队列):队列长度不受限制,当请求越来越多时(任务处理速度跟不上任务提交速度造成请求堆积)可能导致内存占用过多或OOM
ArrayBlockintQueue(有界队列):队列长度受限,当队列满了就需要创建多余的线程来执行任务

handler拒绝策略

AbortPolicy:中断抛出异常
DiscardPolicy:默默丢弃任务,不进行任何通知
DiscardOldestPolicy:丢弃掉在队列中存在时间最久的任务
CallerRunsPolicy:让提交任务的线程去执行任务(对比前三种比较友好一丢丢)

关闭线程池

shutdownNow():立即关闭线程池(暴力),正在执行中的及队列中的任务会被中断,同时该方法会返回被中断的队列中的任务列表
shutdown():平滑关闭线程池,正在执行中的及队列中的任务能执行完成,后续进来的任务会被执行拒绝策略
isTerminated():当正在执行的任务及对列中的任务全部都执行(清空)完就会返回true

springboot使用线程池

/**
 * @Description 配置类中构建线程池实例,方便调用
 */
@Configuration
public class ThreadPoolConfig {
    @Bean(value = "threadPoolInstance")
    public ExecutorService createThreadPoolInstance() {
        //通过guava类库的ThreadFactoryBuilder来实现线程工厂类并设置线程名称
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("thread-pool-%d").build();
        ExecutorService threadPool = new ThreadPoolExecutor(10, 16, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100), threadFactory, new ThreadPoolExecutor.AbortPolicy());
        return threadPool;
    }
}
  //通过name=threadPoolInstance引用线程池实例
  @Resource(name = "threadPoolInstance")
  private ExecutorService executorService;
  @Override
  public void spikeConsumer() {
    //TODO
    executorService.execute(new Runnable() {
    @Override
    public void run() {
      //TODO
     }});
  }

.其它相关

在ThreadPoolExecutor类中有两个比较重要的方法引起了我们的注意:beforeExecute和afterExecute

 protected void beforeExecute(Thread var1, Runnable var2) {
 }
 
 protected void afterExecute(Runnable var1, Throwable var2) {
}
举报

相关推荐

0 条评论