java线程池学习

java线程池学习

线程池原理的简单理解

线程池原理和银行办理业务差不多

线程池就像银行,有需求的时候给你安排窗口办理业务,人数多了就去等待队列去等待,如果哪个窗口的人办理完业务了,等待队列里面的人就会选择该窗口进行业务办理,(有空闲窗口就会从等待队列里面选取,不会直接从外面选取)如果等到队列都满了,还有人要办理业务,就会额外开一些窗口来继续办理业务,如果全部窗口都打开了,等待队伍也满了,还有需求需要办理的话,就会执行拒绝策略,拒绝后面的需求,并执行相应的策略

开放的窗口1,2,3相当于核心线程池数量,最大线程池数量就是银行里面所有的窗口数量

情况一

当办理业务的人数<开放窗口数量+等待队列里面的数量,就直接进行业务办理

在这里插入图片描述

情况二

最大窗口数量+等待队列里面的数量>当办理业务的人数>开放窗口数量+等待队列里面的数量,就会打开未开放的窗口,让等待队列里面的人进来办理业务,缓解压力(如果有个窗口长期没有人来办理业务,即保持空闲一段时间就会关闭该窗口,避免浪费资源)

在这里插入图片描述

在这里插入图片描述

情况三

当办理业务的人数>最大窗口数量+等待队列里面的数量,即银行无法承受人员压力,就会执行拒绝策略,拒绝后面这部分的人

在这里插入图片描述

线程池的创建和使用(建议自定义创建)

 ExecutorService executorService = new ThreadPoolExecutor(3, 5, 2L,
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

线程池的参数说明

  1. 参数一是:int corePoolSize,核心线程数: 也是线程池中常驻的线程数

  2. 参数二是:int maximumPoolSize,最大线程数:在核心线程数的基础上可能会额外增加一些非核心线程

    需要注意的是只有当workQueue队列填满时才会创建多于corePoolSize的线程(线程池总线程数不超过maxPoolSize)

  3. 参数三是:long keepAliveTime,线程存活时间(但有线程长时间没有使用会对这个线程进行销毁):非核心线程的空闲时间超过keepAliveTime就会被自动终止回收掉,注意当corePoolSize=maxPoolSize时,keepAliveTime参数也就不起作用了(因为不存在非核心线程)

  4. 参数四是:TimeUnit unit,keepAliveTime的时间单位

  5. 参数五是:BlockingQueue<Runnable> workQueue,等待队列(要设置队列长度) :可以为无界、有界、同步移交三种队列类型之一,当池子里的工作线程数大于corePoolSize时,这时新进来的任务会被放到队列中

  6. 参数六是:ThreadFactory threadFactory,线程工厂:可以不用填写会默认使用Executors.defaultThreadFactory(),也可以使用guava库的ThreadFactoryBuilder来创建

  7. 参数七是:RejectedExecutionHandler handler,拒绝策略:线程池无法继续接收任务(队列已满且线程数达到maximunPoolSize)时的饱和策略,取值有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy

workQueue队列

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

handler拒绝策略

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

关闭线程池

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

常见的几种自动创建线程池方式

自动创建线程池的几种方式都封装在Executors工具类中(但建议使用自定义的方式创建):

  • new FixedThreadPool:使用的构造方式为new ThreadPoolExecutor(var0, var0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()),设置了corePoolSize=maxPoolSize,keepAliveTime=0(此时该参数没作用),无界队列,任务可以无限放入,当请求过多时(任务处理速度跟不上任务提交速度造成请求堆积)可能导致占用过多内存或直接导致OOM异常 没有非核心线程,队列无限长,可以设置核心线程的数量

    在这里插入图片描述

  • new SingleThreadExector:使用的构造方式为new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), var0),基本同newFixedThreadPool,但是将线程数设置为了1,单线程,弊端和newFixedThreadPool一致 没有非核心线程,队列无限长,核心线程数量为1,既当newFixedThreadPool

    核心线程数量为1的时候就是newSingleThreadExector

    在这里插入图片描述

  • new CachedThreadPool:使用的构造方式为new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue()),corePoolSize=0,maxPoolSize为很大的数,同步移交队列,也就是说不维护常驻线程(核心线程),每次来请求直接创建新线程来处理任务,也不使用队列缓冲,会自动回收多余线程,由于将maxPoolSize设置成Integer.MAX_VALUE,当请求很多时就可能创建过多的线程,导致资源耗尽OOM。这里是没有核心线程,无限多的非核心线程,和队列长度为1的队列

    在这里插入图片描述

  • new ScheduledThreadPool:使用的构造方式为new ThreadPoolExecutor(var1, 2147483647, 0L, TimeUnit.NANOSECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue()),支持定时周期性执行,注意一下使用的是延迟队列,弊端同newCachedThreadPool一致

//这里默认拒绝策略为AbortPolicy
private static ExecutorService executor = new ThreadPoolExecutor(10,10,60L, TimeUnit.SECONDS,new ArrayBlockingQueue(10));
private static ThreadFactory threadFactory = new ThreadFactoryBuilder().build();
 
private static ExecutorService executorService = new ThreadPoolExecutor(10, 10, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10), threadFactory, new ThreadPoolExecutor.AbortPolicy());
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("thread-pool-d%").build();

Springboot中使用线程池

@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
     }});
  }

线程池底层源码分析

首先要读懂下面这俩副图,底层原理思想就是基于这俩图实现的

在这里插入图片描述
在这里插入图片描述

1.线程复用

复用的过程主要如上图所示

execute->addWorker()->Worker->t.start()->run()–>runWorker()->task.run()->processWorkerExit()->addWorker()

这里是个循环,会不断的调用,直到task为空

2.优先级

线程池存放是有优先级的,先会存放核心–>队列–>非核心

执行优先级: 核心–>非核心–>队列

3.源码理解图

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

我们来看看Worker里面是如何运行的

在这里插入图片描述

Worker线程开启以后会调用run()方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页