欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

线程池

程序员文章站 2022-05-06 07:54:21
...

一、为什么会有线程池

我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务呢?

所以就有了线程池,程序启动一个新线程的成本较高,而使用线程池可以很好的提高性能。

二、什么是线程池

线程池是指在初始化一个多线程应用程序过程中创建的一个线程集合。

线程池在任务未到来之前,会创建一定数量的线程放入空闲队列中.这些线程都是处于睡眠状态,即均未启动。因此不消耗CPU,只是占用很小的内存空间。

当请求到来之后,线程池给这次请求分配一个空闲线程,把请求传入此线程中运行,进行处理。

当预先创建的线程都处于运行状态时,线程池可以再创建一定数量的新线程,用于处理更多的任务请求。

如果线程池中的最大线程数使用满了,则会抛出异常,拒绝请求。

当系统比较清闲时,也可以通过移除一部分一直处于停用状态的线程。

线程池中的每个线程都有可能被分配多个任务,一旦任务完成,线程回到线程池中并等待下一次分配任务。

使用线程池可以提升性能,减少CPU资源的消耗,同时还可以控制活动线程,防止并发线程过多,避免内存消耗过度.

二、继承关系

Executor的框架图:
线程池

1、接口和类
  • 接口: Executor、CompletionService,ExecutorService、ScheduledExecutorService
  • 抽象类:AbstractExecutorService
  • 实现类:ExecutorCompletionService,ThreadPoolExecutor,ScheduledThreadPoolExecutor
2、主要方法
  • Executor
    线程池体系的顶层接口。
    该体系内的所有类,接口都默认实现 / 继承此接口,并在此基础上进行分类扩展。
    void execute(Runnable command):在未来某个时间执行Runnable任务
    
  • ExecutorService
    void shutdown():有序关闭已经提交的任务,但不接受新任务,重复shotdown无效
    
    boolean	isShutdown() :判断线程池是否关闭,关闭则返回 true
    
    List<Runnable> shutdownNow() :试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表
    
    <T> Future<T> submit(Callable<T> task):提交有返回值的Runnable任务用于执行,返回一个表示任务的未决结果的 Future
         
    Future<?> submit(Runnable task):提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future
    
    <T> Future<T> submit(Runnable task, T result):提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future
          
    boolean awaitTermination(long timeout, TimeUnit unit):当调用shotdown()方法后,调用此方法可以设置等待时间,等待执行中的任务全部结束,全部结束返回true.如果超时,或线程中断导致未全部结束则返回false.       
    
    boolean isTerminated():当调用了showdown()方法后,如果后所有任务都已完成,则返回 true。注意: 如果不事先调用showdown()方法,则此方法永远返回false.
    
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks):执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。  (一个任务结束有两种情况:1.正常执行完成;2.抛出异常.<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit):执行给定的任务,当所有任务完成或超时期满时(无论哪个首先发生),返回保持任务状态和结果的 Future 列表。 
    
    <T> T invokeAny(Collection<? extends Callable<T>> tasks):执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果。
    
    <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit):执行给定的任务,如果在给定的超时期满前某个任务已成功完成(也就是未抛出异常),则返回其结果。       
    

三、ThreadPoolExecutor

1、构造参数
  • corePoolSize:核心线程池的大小
    如果核心线程池有空闲位置,这是新的任务就会被核心线程 池新建一个线程执行,执行完毕后不会销毁线程,线程会进入缓存队列等待再次被运行。
  • maximunPoolSize:线程池能创建最大的线程数量
    如果核心线程池和缓存队列都已经满了,新的任务进来就会创建新的线程来执行。但是数量不能超过maximunPoolSize,否侧会采取拒绝接受任务策略。
  • keepAliveTime:非核心线程能够空闲的最长时间
    超过时间,线程终止。
    这个参数默认只有在线程数量超过核心线程池大小时才会起作用。只要线程数量不超过核心线程大小,就不会起作用。
  • unit:时间单位
    keepAliveTime的时间单位,有下面几种取值:
    TimeUnit.DAYS;               //天
    TimeUnit.HOURS;             //小时
    TimeUnit.MINUTES;           //分钟
    TimeUnit.SECONDS;           //秒
    TimeUnit.MILLISECONDS;      //毫秒
    TimeUnit.MICROSECONDS;      //微妙
    TimeUnit.NANOSECONDS;       //纳秒
    
  • workQueue:阻塞队列
    用来存放等待被执行的任务,一般有以下三种选择策略:
    ArrayBlockingQueue;
    LinkedBlockingQueue;
    SynchronousQueue;
    
  • threadFactory:线程工厂
    用来创建线程
  • handler:拒绝处理策略
    线程数量大于最大线程数就会采用拒绝处理策略,有以下四种策略:
    ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
    ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
    ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 
    
2、构造方法
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
 BlockingQueue<Runnable> workQueue) 
用给定的初始参数和默认的线程工厂及被拒绝的执行处理程序创建新的 ThreadPoolExecutor

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, 
BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) 
用给定的初始参数和默认的线程工厂创建新的 ThreadPoolExecutor。

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
 TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory) 
用给定的初始参数和默认被拒绝的执行处理程序创建新的 ThreadPoolExecutor。

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, 
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, 
RejectedExecutionHandler handler) 
用给定的初始参数创建新的 ThreadPoolExecutor。

四、Executors静态工厂四种常用线程池

  • newSingleThreadExecutor():单线程池

这个线程池只有一个线程在工作(如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。)
此线程池能够保证所有任务的执行顺序按照任务的提交顺序执行,同一时段只有一个任务在运行。

使用场景: 需要保证执行顺序的场合。

源码:

public static ExecutorService newSingleThreadExecutor() {
	  return new FinalizableDelegatedExecutorService
	     (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,
	                 new LinkedBlockingQueue<Runnable>()));
	    }	    
  • newCachedThreadPool():缓冲功能的线程

    创建一个可缓存的线程池,应用中存在的线程数可以无限大

    必要的时候创建新线程来处理请求,也会重用线程池中已经处于可用状态的线程。
    如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程;
    当任务数增加时,此线程池又可以智能的添加新线程来处理任务。
    此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

    使用场景:耗时短,不需要考虑同步的场合。

    源码:

    public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>());
        }
    
  • newFixedThreadPool:固定线程数量的线程池

    创建固定大小的线程池,以*队列方式运行。

    线程池满且线程都为活动状态的时候如果有新任务提交进来,它们会等待直到有线程可用。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。显式调用shutdown将关闭线程池。

    使用场景:常用场合。

    源码:

        public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    
  • newScheduledThreadPool:定长线程池

    创建一个定长线程池,核心线程数固定,非核心线程数没有限制,并且非核心线程闲置时会被回收。

    使用场景:定时以及周期性执行任务的场合。

    源码:

        public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
            return new ScheduledThreadPoolExecutor(corePoolSize);
        }
    

使用方法可参考此博客 线程池基础知识