• 线程池整理


    线程池

    定义

    线程池,是一种线程的使用模式,它为了降低线程使用中频繁的创建和销毁所带来的资源消耗与代价。
    通过创建一定数量的线程,让他们时刻准备就绪等待新任务的到达,而任务执行结束之后再重新回来继续待命。

    这就是线程池最核心的设计思路,「复用线程,平摊线程的创建与销毁的开销代价」。

    线程池优势

    第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

    第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

    第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

    Java中的线程池

    除以上接口和类,还有一个工具类Executors

    Executor

    //接收一个任务并执行
    void execute(Runnable command)
    

    ExecutorService

    提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成Future的方法。

    线程池的关闭

    //启动一次顺序关闭,执行以前提交的任务,不接受新任务。如果已经关闭,则调用没有其他作用。
    //如果继续调用execute方法执行新的任务的话就会抛出RejectedExecutionException异常。(submit方法也会抛出上述异常)
    void shutdown()
        
    //试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。 
    //无法保证能够停止正在处理的活动执行任务,但是会尽力尝试。例如,通过 Thread.interrupt() 来取消典型的实现,所以任何任务无法响应中断都可能永远无法终止。
    //上面这句话的意思是向所有执行中的线程发出interrupted以中止线程的运行,这时,各个线程会抛出InterruptedException异常(前提是线程中运行了sleep等会抛出异常的方法)
    List<Runnable> shutdownNow()
        
    //请求关闭、发生超时或者当前线程中断,无论哪一个首先发生之后,都将导致阻塞,直到所有任务完成执行。
    //timeout - 最长等待时间
    //unit - timeout 参数的时间单位
    //这个方法的意思是设定一个时间,当到达这个时间时,如果线程池里还有没有运行完的任务,就返回false,如果任务全部运行完了,就会返回true
    //awaitTermination方法也不是在它被调用的时间点上简单得等待任务结束而是在awaitTermination方法调用后,持续监视各个任务的状态以或者是否线程已经运行结束。所以不调用shutdown方法执行调用awaitTermination的话由于追加出来的任务可能会导致任务状态监视出现偏差而发生预料之外的awaitTermination的Timeout异常
    boolean awaitTermination(long timeout,
                             TimeUnit unit)
                             throws InterruptedException
        
    //以上三个方法往往是搭配起来用来关闭线程池的,如以下
        //用Executors里的静态方法创建一个线程池
        ExecutorService pool = Executors.newFixedThreadPool(5); 
    	//省略执行任务的过程...直接到达关闭线程池到的时候
    	try {
                // 发出关闭线程池的信号,并让线程池不再接受新任务
                pool.shutdown();
    
                // 指定一个执行的时间,到达时间后如果全部执行完毕就当无事发生,shoudown()会把线程池关闭,
            	// 如果还有任务没有执行完,那么awaitTermination返回false,就给当前执行任务的线程发送interrupted,执行任务的线程就会捕捉到这个异常,停止执行任务,去处理异常
                if(!pool.awaitTermination(awaitTime, TimeUnit.MILLISECONDS)){
                    // 超时的时候向线程池中所有的线程发出中断(interrupted)。
                    pool.shutdownNow();
                }
            } catch (InterruptedException e) {
                // awaitTermination方法被中断的时候也中止线程池中全部的线程的执行。(还没遇到过这种情况)
                System.out.println("awaitTermination interrupted: " + e);
                pool.shutdownNow();
            }
    

    查看当前执行的状态

    //如果此执行程序已关闭,则返回 true。 
    boolean isShutdown()
        
    //如果关闭后所有任务都已完成,则返回 true。注意,除非首先调用 shutdown 或 shutdownNow,否则 isTerminated 永不为 true。
    boolean isTerminated()
    

    关于执行任务的方法

    //执行一个实现了Callable接口的任务,Futrue用来接收执行的结果,该Future的get方法在成功完成时将会返回该任务的结果
    <T> Future<T> submit(Callable<T> task)
        
    //和上面一样,但是为什么Runnable接口任务会有返回值呢?举一个例子,把result传入task里,task里可以对result进行修改
    <T> Future<T> submit(Runnable task,
                         T result)
    
    //提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。该 Future 的 get 方法在成功 完成时将会返回 null。
    Future<?> submit(Runnable task)
        
        //接下来是批量执行任务的方法
    
    //执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
                              throws InterruptedException
    
    //加上了一个限时
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout,
                                  TimeUnit unit)
                              throws InterruptedException
    
    //执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果。一旦正常或异常返回后,则取消尚未完成的任务。
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
                throws InterruptedException,
                       ExecutionException
    
    //加上了一个限时
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout,
                    TimeUnit unit)
                throws InterruptedException,
                       ExecutionException,
                       TimeoutException  //到达了设定的时间没有任务完成会抛出
    

    AbstractExecutorService

    一个抽象类,里面的方法和ExecutorService大同小异,可以去看看JDK API

    ScheduledExecutorService

    可安排在给定的延迟后运行或定期执行的命令

    //在给定的延迟后开始执行这个任务
    ScheduledFuture<?> schedule(Runnable command,
                                long delay,
                                TimeUnit unit)
        
    <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                    long delay,
                                    TimeUnit unit)
    
    //下面这两个都是在经过初始延迟后周期性执行任务的方法
        
        //不同的地方是,这个方法是从任务开始执行时计算时间
        //如果此任务的任何一个执行要花费比其周期更长的时间,则将推迟后续执行,但不会同时执行
    ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                           long initialDelay,
                                           long period,
                                           TimeUnit unit)
        //而这个方法是从任务执行完开始计算时间
    ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                              long initialDelay,
                                              long delay,
                                              TimeUnit unit)
    

    线程池的具体实现

    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,则新建的是核心线程,如果超过corePoolSize,则新建的是非核心线程

    核心线程默认情况下会一直存活在线程池中,即使处于空闲状态也不会销毁

    可以指定ThreadPoolExecutor的allowCoreThreadTimeOut 为true,那么核心线程空闲超过一定时间会销毁

    int maximumPoolSize

    线程池中最大线程数量 = 核心线程数 + 非核心线程数

    long keepAliveTime

    非核心线程空闲最长时间

    如果设置allowCoreThreadTimeOut = true,则会作用于核心线程

    TimeUnit unit

    设置空闲时间的单位

    BlockingQueue workQueue

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

    当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务

    常见workQueue类型

    1.SynchronousQueue(直接提交):这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大

    2.LinkedBlockingQueue(无界队列):这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize

    3.ArrayBlockingQueue(有界队列):可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误

    4.DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务

    5. PriorityBlockingQueue(优先任务队列):是一个无界的特殊队列,可以控制任务执行的先后顺序,而上边几个都是先进先出的策略。

    ThreadFactory threadFactory

    用来创建线程的方法

    RejectedExecutionHandler handler

    任务拒绝策略(可以重写)

    当线程池中所以线程都在工作并且任务队列已满的情况下再传任务会触发任务拒绝策略
    默认的 ThreadPoolExecutor.AbortPolicy 中,处理程序遭到拒绝将抛出运行时 RejectedExecutionException。
    ThreadPoolExecutor.CallerRunsPolicy 中,用调用者所在线程执行任务
    ThreadPoolExecutor.DiscardPolicy 中,不能执行的任务将被删除。
    ThreadPoolExecutor.DiscardOldestPolicy 中,如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,将自己排到队尾,然后重试执行程序(如果再次失败,则重复此过程)。

    线程池运行流程

    1.线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务

    2.线程数量达到了corePools,则将任务移入队列等待

    3.队列已满,新建线程(非核心线程)执行任务

    4.队列已满,总线程数又达到了maximumPoolSize,就会由(RejectedExecutionHandler)抛出异常

    线程池运行流程

    线程池execute执行流程

    ThreadPoolExecutor其他方法

    多半是用来监控线程池当前状态和设置线程池参数的

    ScheduledThreadPoolExecutor

    比较重要的方法可以参见ScheduledExecutorService接口

    Executors工具类

    介绍一下创建的四种线程池

    CachedThreadPool

    可缓存线程池:

    ​ 1.线程数无限制

    ​ 2.有空闲线程则复用空闲线程,若无空闲线程则新建线程

    ​ 3.一定程序减少频繁创建/销毁线程,减少系统开销

    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    

    FixedThreadPool

    定长线程池:

    ​ 1.可控制线程最大并发数(同时执行的线程数)

    ​ 2.超出的线程会在队列中等待

    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads);
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(int nThreads, ThreadFactory threadFactory);
    
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    

    ScheduledThreadPool

    定长线程池:

    支持定时及周期性任务执行。

    ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(int corePoolSize);
    
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    
    //ScheduledThreadPoolExecutor():
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }
    

    SingleThreadExecutor

    单线程化的线程池:

    ​ 1.有且仅有一个工作线程执行任务

    ​ 2.所有任务按照指定顺序执行,即遵循队列的入队出队规则

    ExecutorService singleThreadPool = Executors.newSingleThreadPool();
    
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    

    FixedThreadPool和SingleThreadExecutor使用的任务队列都是无界队列

    CachedThreadPool和ScheduledThreadPool可允许创建的线程数都是Integer.MAX_VALUE

    这都会导致OOM,所以避免使用Executors创建线程池

    线程池线程数量计算

    假设CPU核心个数为N

    如果是CPU密集型应用,则线程池大小设置为N+1

    如果是IO密集型应用,则线程池大小设置为2N+1

    但是实际中并不是只有一种任务,所以有多种任务该如何计算

    1. ​ Threads = N * U * (1 + W/C)

    U为CPU利用率

    W/C为等待时间与计算时间比率

    1. ​ Threads = N(可用核心数) / (1 - 阻塞系数)

      阻塞系数 = W/(W+C)

      W为等待实际,C为计算时间

    两种方法的计算结果是一样的

  • 相关阅读:
    登录后返回到登录页问题
    vue组件插槽
    js定义类
    arguments.callee用法
    深拷贝的原生js实现
    Hybrid APP架构设计思路
    使用vlfeat 包中遇到的问题
    tensorflow faster rcnn 代码分析一 demo.py
    labelImg 工具
    faster rcnn 做识别
  • 原文地址:https://www.cnblogs.com/Jiewl/p/12723435.html
Copyright © 2020-2023  润新知