美文网首页
线程池的那些事儿——java基础

线程池的那些事儿——java基础

作者: 9dfaf364d57f | 来源:发表于2018-02-27 22:19 被阅读51次

前言

线程池的产生是为了管理线程,线程的频繁创建或不限制的创建会对系统带来相当大的资源损耗。在JAVA1.5中,提供了Executor框架用于将任务的提交和执行解耦,任务提交由Runnable或Callable处理,而Executor框架来处理任务。

线程介绍:https://www.jianshu.com/p/18e20701974a

ThreadPoolExecutor

ThreadPoolExecutor是线程池的核心实现类。其构造方法有4个,我们选最多参数的构造方法讲解:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

corePoolSize:核心线程数。如果当前运行的线程数小于corePoolSize,则创建新的线程来处理任务;如果大于corePoolSize,则不会再继续创建。如果调用prestartAllcoreThread方法,线程池会提前创建并启动所有核心线程来等待任务;否则,默认情况下,只有提交了任务才会创建线程。
maximumPoolSize:线程池允许创建的最大线程数。如果任务队列满了并且线程池中线程数小于maximumPoolSize,则会仍旧会创建线程来进行处理任务,此时创建的线程称为非核心线程,此时创建的线程存活时间会受keepAliveTime约束。
keepAliveTime:非核心线程闲置时间。线程闲置超过这个时间会被回收,所以如果任务比较多,且每个任务执行用时比较短时,可以考虑增大keepAliveTime值来提高利用率。值得一提是,如果通过设置allowCoreThreadTimeOut(true),keepAliveTime也会应用到核心线程。
unit:keepAliveTime的时间单位。可选参数为:天(DAYS)、小时(HOURS)、分钟(MINUTES)、秒(SECONDS)、毫秒(MILLISECONDS)等。
workQueue:任务队列。如果当前线程大于corePoolSize,则将任务添加至此任务队列中,类型为BlockingQueue,是一个阻塞队列。
threadFactory:线程工厂。可以用线程工厂给每个创建出来的线程设置名字。
RejectedExecutionHandler:饱和策略。当任务队列和线程池都满了时所采取的应对策略。有如下4中策略:

  1. AbordPolicy:默认值,表示无法处理新任务,并抛出
  2. CallerRunsPolicy:用调用者所在的线程处理任务。
  3. DiscardPolicy:不能执行的任务,并将该任务删除。
  4. DiscardOldestPolicy:丢弃队列最近的任务,并执行当前任务。

线程池的工作流程

线程池的工作流程

图中已经表现的非常清楚,便不再多说,只是顺带一提的是,如果任务队列中有任务在等待执行的话,核心线程执行完任务后会从任务队列中取任务进行执行,直至队列为空。

四种常用线程池

这四种常用线程池,其实无非是对ThreadPoolExecutor的参数进行不同的配置而产生,他们分别为:FixedThreadPool、CachedThreadPool、SingleThreadPool、ScheduledThreadPool。

1、FixedThreadPool

在Executors类的newFixedThreadPool方法中,可以看到如下一段代码,这段代码便是创建FixedThreadPool线程池。从参数可知,核心线程数和线程池线程总数均为nThreads,即此线程池无非核心线程,采用的阻塞队列为无界阻塞队列

ThreadPoolExecutor(nThreads, nThreads,
           0L, TimeUnit.MILLISECONDS,
           new LinkedBlockingQueue<Runnable>());
FixedThreadPool执行示意图
2、CachedThreadPool

在Executors类中的newCachedThreadPool方法中,可看到CachedThreadPool的构造参数。该线程池无核心线程,均为非核心线程,线程数量为Integer.MAX_VALUE,即无上限,空闲等待时间为60秒。阻塞队列用了不存储元素的SynchronousQueue,每个插入操作必须等待另一个线程的移除操作,同样任何一个移除操作都等待另一个线程的插入操作。此线程池比较适用于大量的需要立即处理的但耗时比较少的任务。

ThreadPoolExecutor(0, Integer.MAX_VALUE,
           60L, TimeUnit.SECONDS,
           new SynchronousQueue<Runnable>());
CachedThreadPool执行示意图
3、SingleThreadExecutor

在Executors类中的newSingleThreadExecutor方法中,可看到SingleThreadExecutor的构造参数。该线程池只有一个核心线程,无非核心线程。阻塞队列为无界阻塞队列

new ThreadPoolExecutor(1, 1,
                 0L, TimeUnit.MILLISECONDS,
                 new LinkedBlockingQueue<Runnable>()));
SingleThreadExecutor执行示意图
4、ScheduledThreadPool

ScheduledThreadPool是一个能实现定时和周期性任务的线程池,在Executors中的ScheduledExecutorService方法中,创建源码如下:

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

源码中创建的ScheduledThreadPoolExecutor类是继承自ThreadPoolExecutor,用于给定时处理任务。ScheduledThreadPoolExecutor构造函数如下,可以看出核心线程数由入参决定,非核心线程数为无限,但采用阻塞队列DelayedWorkQueue是无界,所以无非核心线程。

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

与前三种线程池有些不同的是,当执行ScheduledThreadPoolExecutor的scheduleAtFixedRate或scheduleWithFixedDelay方法时,会向DelayedWorkQueue添加一个实现RunnableScheduleFuture接口的ScheduleFutureTask,并检查运行的线程数量是否已达到corePoolSize。如果还未达到,则创建新的线程并启动它,但不会立即去执行任务,而是去DelayedWorkQueue中取ScheduleFutureTask,然后执行任务。如果运行的线程数已经达到corePoolSize,则将任务添加到DelayedWorkQueue中。DelayedWorkQueue会将任务进行排序,先执行的任务放在队列前面。最重要的是,执行完的任务,会将ScheduleFutureTask中的time变量改为下次执行的时间放回到DelayedWorkQueue中,以此达到循环定时的功能。

ScheduledThreadPool执行示意图

相关文章

网友评论

      本文标题:线程池的那些事儿——java基础

      本文链接:https://www.haomeiwen.com/subject/haexxftx.html