撩一撩Java线程池ThreadPoolExecutor

什么是ThreadPoolExecutor?

JDK1.5开始出现,继承关系如下:

ThreadPoolExecutor <- AbstractExecutorService <-ExecutorService <- Executor

后面两个是接口,官方注释表明主要是解决两个问题:
1.当执行大量异步任务时候线程池能够提供较好的性能,这是因为使用线程池可以使每个任务的调用开销减少。
2.线程池提供了一种资源限制和管理的手段,比如当执行一系列任务时候对线程的管理,每个ThreadPoolExecutor也保留了一些基本的统计数据,比如当前线程池完成的任务数目。

为什么系统会给我们提供线程池呢?
新建线程的缺点如下:
a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比new Thread,四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。

如何使用ThreadPoolExecutor

1.使用Executors工具类(让我想到了Collections)里面已有的静态方法创建

        val newFixedThreadPool = Executors.newFixedThreadPool(20)
        val newSingleThreadExecutor = Executors.newSingleThreadExecutor()
        val newCachedThreadPool = Executors.newCachedThreadPool()
        val newScheduledThreadPool = Executors.newScheduledThreadPool(20)

2.ThreadPoolExecutort提供了四个构造方法,我们可以直接创建自定义的线程池,方法1 本质上也是通过ThreadPoolExecutort创建的,只是帮我们封装好了。


构造方法

相关参数解释如下:

序号 参数名 参数类型 参数含义
1 corePoolSize int 核心线程池大小
2 maximumPoolSize int 最大线程池大小
3 keepAliveTime long 线程最大空闲时间
4 unit TimeUnit 时间单位
5 workQueue BlockingQueue 线程等待队列
6 threadFactory ThreadFactory 线程创建工厂
7 handler RejectedExecutionHandler 拒绝策略

我们先从系统提供的方法来深入了解下每个参数的含义

1.newFixedThreadPool(20)

     public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue());
    }

这个方法有1 个参数,int threads 表示线程池中的数量,这个参数会直接给ThreadPoolExecutor的第一和第二个参数,也就是corePoolSize和maximumPoolSize,剩下的参数中空余时间给的是 0,创建了一个新建了阻塞队列LinkedBlockingQueue,返回值是ExecutorService。

2.newSingleThreadExecutor()

   public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue()));
    }

这个方法没有参数,和newFixedThreadPool区别是corePoolSize和maximumPoolSize两个参数给了都是 1,需要注意这里最终返回的是FinalizableDelegatedExecutorService。有个问题想一想,一个线程也要用线程池么?意义在哪里?

3.newCachedThreadPool()

   public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue());
    }

这个看起来参数都挺全的,首先corePoolSize是 0,maximumPoolSize给了 int 最大值,keepAliveTime是 60 ,时间单位是秒,这里注意等待队列和前面两个不一样这里是SynchronousQueue。返回值是ExecutorService。

4.newScheduledThreadPool(20)

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

这个高级点了,自己是个对象当然是集成ThreadPoolExecutor类,我们看下他的构造方法有两个,这里直接看一个参数的构造方法。返回值是ScheduledThreadPoolExecutor,这里注意和前面三个都不一样。

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

同样这里入参和清晰了,参数名是corePoolSize核心线程数量直接给了我们ThreadPoolExecutor的第一个参数corePoolSize,第二个maximumPoolSize最大线程池大小和newCachedThreadPool一样给的是 int 最大值,第三个看起来是个常量我们看下代码中是 DEFAULT_KEEPALIVE_MILLIS = 10L ,单位是秒,这里的等待队列和前面的也不一样是DelayedWorkQueue优先级队列。

到此系统提供的四种创建线程池的参数都一一看了一遍,接下来我们看下为什么系统要提供这四种方法给我们,根据构造方法中的参数,有什么不一样的使用场景,可以先想一想不着急忘记下。

现在就开始分析下四个方法创建出的线程池的使用场景。

1. newFixedThreadPool(白话文:固定数目的线程池)

通过上面的构造方法我们知道,newFixedThreadPool核心线程池等于最大线程池,当前的线程数能够比较稳定保证一个数。能够避免频繁回收线程和创建线程。故适用于处理cpu密集型的任务,确保cpu在长期被工作线程使用的情况下,尽可能少的分配线程,即适用长期的任务。
缺点:
达到线程池最大容量后,如果有任务完成让出占用线程,那么此线程就会一直处于等待状态,而不会消亡,直到下一个任务再次占用该线程。这就可能会使用无界队列来存放排队任务,当大量任务超过线程池最大容量需要处理时,队列无线增大,会占用大量资源。

2. newSingleThreadExecutor(白话文:唯一线程线程池)

由于是唯一的嘛,最适用串行化任务。

3. newCachedThreadPool(白话文:可缓存线程的线程池)

newCacehedThreadPool 的最大特点就是,线程数量不固定。只要有空闲线程空闲时间超过keepAliveTime,就会被回收。有新的任务,查看是否有线程处于空闲状态,如果不是就直接创建新的任务。故适用用于并发不固定的短期小任务。
缺点:
线程池没有最大线程数量限制,如果大量的任务同时提交,可能导致创线程过多会而导致资源耗尽。

4. newScheduledThreadPool(白话文:定时及周期执行的线程池)

适用于定时操作一些任务

你可能感兴趣的:(撩一撩Java线程池ThreadPoolExecutor)