线程池基础

156 阅读3分钟

为什么要用线程池

  1. 创建/销毁线程的系统开销过于庞大,频繁的执行这两个操作,会在很大程度上影响处理效率
  2. 线程并发数量过多,抢占系统资源从而导致堵塞
  3. 更加方便快捷的对线程进行统一管理

线程池的创建

线程池类:ThreadPoolExecutor

内含四个构造函数:

//五个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)

//六个参数的构造函数-1
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)

//六个参数的构造函数-2
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler)

//七个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

各参数含义:

  • int corePoolSize 该线程池中核心线程数最大值

    核心线程:

    ​ 在线程池新建线程时,若核心线程数小于核心线程最大值(corePoolSize),则新建的线程是核心线程,否则是非核心线程

  • int maximumPoolSize 该线程池中线程总数最大值

    线程池线程总数 = 核心线程总数 + 非核心线程总数

  • long keepAliveTime 非核心线程闲置超时时长

    非核心线程若是在线程池中的闲置时间超过这个时间,就会自动销毁

  • TimeUnit unit 一个枚举单位

    与上面的keepAliveTime互动

    NANOSECONDS : 1微毫秒 = 1微秒 / 1000

    MICROSECONDS : 1微秒 = 1毫秒 / 1000

    MILLISECONDS : 1毫秒 = 1秒 /1000

    SECONDS : 秒

    MINUTES : 分

    HOURS : 小时

    DAYS : 天

  • BlockingQueue workQueue 线程池中的任务队列

    线程池中的任务队列: 维护等待执行的Runnable对象

    当所有的核心线程都在执行的时候,新建的任务会被存放在任务队列中,等待核心线程的执行;若任务队列满了,则会新建非核心线程执行任务(也就不插入到任务队列啦)

  • ThreadFactory threadFactory 线程创建的方式

    主要就是给线程起名字

  • RejectExecutionHandler handler 任务拒绝策略

    下文有专门的讲的地方

线程池如何执行的

  1. 默认情况下,线程池中没有线程,除非用了一些特定方法,预创建了一些线程(prestartAllCoreThreads()等)

  2. 任务出现的时候,当**线程(核心线程)数量未达到CorePoolSize的时候,则新建一个线程(核心线程)**执行任务

    ps:如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都属于空闲状态,也要求创建新的线程来处理被添加的任务

  3. 线程(核心线程)数量达到corePookSize的时候,将任务移入任务队列,等待核心线程空出来,在依次执行任务队列中的任务

  4. 任务队列已经满了的话,任务就不等核心线程来处理啦,而是由线程池新建一个**线程(非核心线程)**来处理这个任务

  5. 队列已满,线程总是也达到了maximumPoolSize,抛出

    异常(RejectedExecutionHandler handler)

如何在线程池中添加任务

很简单,一句代码就OK了

ThreadPoolExecutor.execute(Runnable r);

任务拒绝策略

产生原因

线程池的任务已经完全满了,即线程池中所有线程(核心+非核心)已经达到最大数量,并全部在进行工作,任务队列也已经排满了,那么此时,再有任务execute到线程池的时候,我们需要拒绝任务。

拒绝策略(一般四种)

  1. 默认方法(defaultHandler):AbortPolicy

    抛弃新任务 并 抛出RejectedExecutionException异常

  2. DiscardPolicy:

    也是丢弃任务但是不抛出异常

  3. DiscardOldestPolicy:

    丢弃队列最前面的任务,然后重新尝试执行此任务(重复此过程)

  4. CallerRunsPolicy:

    由调用线程执行此任务