java中的线程池ThreadPoolExecutor

java中的线程池

  • 什么是线程池
  • JDK对线程池的支持
    • 线程池工厂Executors
    • 计划任务ScheduledExecutorService
    • 核心线程池的内部实现
    • 线程池常用方法

什么是线程池

  1. 虽然与进程相比,线程是一种轻量级的工具,但其创建和关闭仍然需要花费时间。当线程数量过大时,反而会耗尽CPU和内存资源,大量的线程回收也会给GC带来很大的压力,延长GC的停顿时间。
  2. 线程池中,总有那么几个活跃线程。当你需要使用线程时,可以从池子中随便拿一个空闲线程,当完成工作时,并不着急关闭线程,而是将这个线程退回到池子中,方便下一次使用。
    java中的线程池ThreadPoolExecutor_第1张图片

JDK对线程池的支持

  1. JDK提供了一套Executor框架,本质就是一个线程池。核心成员如下图:
    java中的线程池ThreadPoolExecutor_第2张图片
  2. Executor接口中有一个抽象方法void execute(Runnable command);
  3. ThreadPoolExecutor表示一个线程池,ThreadPoolExecutor类实现了Executor接口,通过这个接口,任何Runnable的对象都可以被ThreadPoolExecutor线程池进行调度。

线程池工厂Executors

  1. Executors类扮演线程池工厂的角色,通过Executors可以取得一个拥有特定功能的线程池。
    java中的线程池ThreadPoolExecutor_第3张图片
  2. newFixedThreadPool()方法:该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
  3. newSingleThreadExecutor()方法:该方法返回一个只有一个线程的线程池。若多于一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
  4. newCachedThreadPool()方法:该方法返回一个可根据实际情况调整数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
  5. newSingleThreadScheduledExecutor()方法:该方法返回一个ScheduledExecutorService对象,线程池大小为1.ScheduledExecutorService接口在ExecutorService接口之上扩展了在给定时间执行某任务的功能,如在某个固定的延时之后执行,或者周期性执行某个任务。
  6. newScheduledThreadPool()方法:该方法也返回一个ScheduledExecutorService对象,但该线程池可以指定线程数量。

计划任务ScheduledExecutorService

  1. ScheduledExecutorService可以根据时间需要对线程进行调度。
  2. 主要方法
    java中的线程池ThreadPoolExecutor_第4张图片
  3. schedule()会在给定时间,对任务进行一次调度。
  4. public ScheduledFuture scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);会对任务进行周期性调度,它是以上一个任务开始执行时间为起点,之后的period时间,调度下一次任务。如果任务执行时间超过period时间的话,下一次任务会直到上一次任务完成才进行调度。
  5. public ScheduledFuture scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);也是对任务进行周期性调度,它是在上一个任务结束后,再等待delay时间后才会对任务进行调度。
  6. 周期性任务一旦遇到异常,那么后续的所有子任务都会停止调度,所以需要做好异常处理。

核心线程池的内部实现

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

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  1. 由以上线程工厂Executors中的构造线程池的静态方法可以看到,虽然三种线程池的特性都有所不同,但都是基于ThreadPoolExecutor进行的封装;
  2. ThreadPoolExecutor构造方法如下:
public ThreadPoolExecutor(
    int corePoolSize,
    int maximumPoolSize,
    long keepAliveTime,
    TimeUnit unit,
    BlockingQueue<Runnable> workQueue,
    ThreadFactory threadFactory,
    RejectedExecutionHandler handler
) 
1. corePoolSize:线程池中要保留的线程数,即使线程处于空闲状态;
2. maximumPoolSize:线程池中允许的最大线程数
3. keepAliveTime:当线程数大于corePoolSize时,这是多余的空闲线程在终止之前等待新任务的最长时间
4. unit:keepAliveTime的单位;
5. workQueue:在执行任务之前用于保留任务的队列。(有界、无界、优先)
6. threadFactory:使用怎样的工厂类创建一个线程
7. handler:当任务无法被执行时使用的拒绝策略(队列无法存储更多的任务并且线程池已达到允许的最大线程数)
  1. 参数workQueue指被提交但未执行的任务队列,是一个BlockingQueue接口的对象,仅用于存放Runnable对象;
    1. 直接提交队列SynchronousQueue:该队列没有容量,每一个插入操作都要等待一个相应的删除操作,每一个删除操作都要等待对应的插入操作;任务不会被真实的保存,而总是将新任务提交给线程执行,如果没有空闲的线程,则尝试创建新的。因此使用SynchronousQueue队列ie,通常要设置很大的maximumPoolSize值。
    2. 有界的任务队列ArrayBlockingQueue,构造函数有一个队列容量值设置;当有新的任务需要执行时,线程池数量小于corePoolSize则直接创建一个线程执行任务,否则的话就把任务加入等待队列。队列已满的话,若线程数量小于maximumPoolSize,创建新的线程执行任务,若大于maximumPoolSize,则执行拒绝策略;
    3. 无界任务队列LinkedBlockingQueue,与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。具体的任务执行策略与有界队列相似;若任务创建速度很快,处理很慢,那么无界队列会保持快速增长,内存资源很容易被耗尽。
    4. 优先任务队列PriorityBlockingQueue,优先任务队列时可以控制任务的执行先后顺序,是一个特殊的无界队列;
  2. 拒绝策略handler:
    java中的线程池ThreadPoolExecutor_第5张图片
    1. AbortPolicy:直接抛出异常,阻止系统正常工作;(默认策略)
    2. CallerRunsPolicy:只要线程池未关闭,该策略直接再调用者线程中,运行当前被丢弃的任务。(任务不会真的被丢弃,但是性能可能会急剧下降)
    3. DiscardOldestPolicy:丢弃最旧的未处理请求,然后重试
    4. DiscardPolicy:静默丢弃,不予任何处理
    5. 自行扩展RejectedExecutionHandler接口,重写void rejectedExecution(Runnable r, ThreadPoolExecutor executor);方法

线程池常用方法

  1. void execute(Runnable command);
  2. Future submit(Runnable task);
  3. void shutdown();该方法并不会立即暴力终止所有任务,会等待所有任务执行完成后再关闭线程池。但是该方法会立即返回,不会等待所有线程执行完成再返回,并且该方法执行后,线程池就不会再接受新任务了。是一个安全关闭线程池的方法。

你可能感兴趣的:(java并发,java,多线程,队列)