zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    【SqlServer系列】表达式(expression)
    【SqlServer系列】语法定义符号解析
    Docker常用命令<转>
    VMware下的Centos7联网并设置固定IP
    redis的setbit命令
    Java并发编程:并发容器之CopyOnWriteArrayList<转>
    Java并发编程:volatile关键字解析<转>
    Java并发编程:阻塞队列 <转>
    Java并发编程:Lock和Synchronized <转>
    Jackson学习笔记(三)<转>
  • 原文地址:https://www.cnblogs.com/bytecodebuffer/p/11953664.html
Copyright © 2011-2022 走看看