Java 线程池

为什么使用线程池

平时我们在使用多线程的时候,通常都是架构师配置好了线程池的 Bean,我们需要使用的时候,提交一个线程即可,不需要过多关注其内部原理。

在学习一门新的技术之前,我们还是先了解下为什么要使用它,使用它能够解决什么问题:

  1. 创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率

    例如:

    记创建线程消耗时间T1,执行任务消耗时间T2,销毁线程消耗时间T3

    如果T1+T3>T2,那么是不是说开启一个线程来执行这个任务太不划算了!

    正好,线程池缓存线程,可用已有的闲置线程来执行新任务,避免了T1+T3带来的系统开销

  2. 线程并发数量过多,抢占系统资源从而导致阻塞

    我们知道线程能共享系统资源,如果同时执行的线程过多,就有可能导致系统资源不足而产生阻塞的情况

    运用线程池能有效的控制线程最大并发数,避免以上的问题

  3. 对线程进行一些简单的管理

    比如:延时执行、定时循环执行的策略等

    运用线程池都能进行很好的实现

创建一个线程池

在 Java 中,新建一个线程池对象非常简单,Java 本身提供了工具类java.util.concurrent.Executors,可以使用如下代码创建一个固定数量线程的线程池:

ExecutorService service = Executors.newFixedThreadPool(10); 

注意:以上代码用来测试还可以,实际使用中最好能够显示地指定相关参数。

我们可以看下其内部源码实现:

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

在阿里巴巴代码规范中,建议我们自己指定线程池的相关参数,为的是让开发人员能够自行理解线程池创建中的每个参数,根据实际情况,创建出合理的线程池。接下来,我们来剖析下java.util.concurrent.ThreadPoolExecutor的构造方法参数。 

ThreadPoolExecutor 浅析

java.util.concurrent.ThreadPoolExecutor有多个构造方法,我们拿参数最多的构造方法来举例,以下是阿里巴巴代码规范中给出的创建线程池的范例:

ThreadPoolExecutor service = new ThreadPoolExecutor(5, 200,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(1024), 
                new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build(), 
                new ThreadPoolExecutor.AbortPolicy());

首先最重要的几个参数,可能就是:corePoolSizemaximumPoolSizeworkQueue了,先看下这几个参数的解释:

  • corePoolSize:核心线程池的大小,如果核心线程池有空闲位置,这是新的任务就会被核心线程池新建一个线程执行,执行完毕后不会销毁线程,线程会进入缓存队列等待再次被运行
  • maximumPoolSize:线程池能创建最大的线程数量。如果核心线程池和缓存队列都已经满了,新的任务进来就会创建新的线程来执行。但是数量不能超过maximunPoolSize,否侧会采取拒绝接受任务策略
  • keepAliveTime:非核心线程能够空闲的最长时间,超过时间,线程终止
  • unit:时间单位,和keepAliveTime配合使用
  • workQueue:缓存队列,用来存放等待被执行的任务
  • handler:拒绝处理策略,线程数量大于最大线程数就会采用拒绝处理策略,四种策略为
  • ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
    ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
    ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 

    Executor接口有一个子接口ExecutorService,ExecutorService的实现类为AbstracExecutorService,而ThreadPoolExcutor正是AbstrcExecutorService的子类。

    ThreadPoolExecutor还有两个常用的方法shutdown,关闭线程池

线程池的优点

  • 重用存在的线程,减少对象的创建、消亡的开销

  • 性能佳可以有效的控制最大的并发线程数,提高系统资源的利用率,避免竞争,堵塞
  • 提供定时执行、单线程并发数控制等

Executors 四种线程池

  • newFiledThreadPool
  • newCachedThreadPool
  • newSingleThreadExecutor
  • newScheduledThreadPool

参考:https://segmentfault.com/a/1190000018399962

参考:https://www.cnblogs.com/zzuli/p/9386463.html

原文地址:https://www.cnblogs.com/bytecodebuffer/p/11953664.html