zoukankan      html  css  js  c++  java
  • 线程池

    一、线程池介绍

    线程池是一种多线程处理形式,处理过程中将任务提交到线程池,任务的执行交由线程池来管理。

    如果每个请求都创建一个线程去处理,那么服务器的资源很快就会被耗尽,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

    如果用生活中的列子来说明,我们可以把线程池当做一个客服团队,如果同时有1000个人打电话进行咨询,按照正常的逻辑那就是需要1000个客服接听电话,服务客户。现实往往需要考虑到很多层面的东西,比如:资源够不够,招这么多人需要费用比较多。正常的做法就是招100个人成立一个客服中心,当有电话进来后分配没有接听的客服进行服务,如果超出了100个人同时咨询的话,提示客户等待,稍后处理,等有客服空出来就可以继续服务下一个客户,这样才能达到一个资源的合理利用,实现效益的最大化。

    二、Java中的线程池种类

    • newSingleThreadExecutor:一个单线程的线程池,可以用于需要保证顺序执行的场景,并且只有一个线程在执行。

    • newFixedThreadPool:一个固定大小的线程池,可以用于已知并发压力的情况下,对线程数做限制。

    • newCachedThreadPool:一个可以无限扩大的线程池,比较适合处理执行时间比较小的任务。

    • newScheduledThreadPool:可以延时启动,定时启动的线程池,适用于需要多个后台线程执行周期任务的场景。

    • newWorkStealingPool:一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行。

    1.自定义一个线程类:

    public class MyThread implements Runnable {
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "	开始发车啦....");
            
        }
    
    }

    2.测试类

    public class ThreadPoolTest {
        public static void main(String[] args) {
            // 1.单线程的线程池
            ExecutorService singlePool = Executors.newSingleThreadExecutor();
            // 2.固定大小的线程池
            ExecutorService fixedPool = Executors.newFixedThreadPool(10);
            // 3.可缓存的线程池
            ExecutorService cacheddPool = Executors.newCachedThreadPool();
            // 4.定时任务的线程池
            ExecutorService scheduleddPool = Executors.newScheduledThreadPool(10);
            // 5.任务队列的线程池jdk1.8才有
            ExecutorService stealingdPool = Executors.newWorkStealingPool();
    
            // 自定义线程池的拒绝策略
            BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100);
            ThreadPoolExecutor executor = new ThreadPoolExecutor(// 使用ThreadPoolExecutor自定义线程池
                    10, 100, 10, TimeUnit.SECONDS, workQueue, new RejectedExecutionHandler() {
    
                        @Override
                        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                            if (e.isShutdown()) {
                                r.run();
                            }
    
                        }
                    });
            for (int i = 0; i < 20; i++) {
                // singlePool.execute(new MyThread());
                // fixedPool.execute(new MyThread());
                // cacheddPool.execute(new MyThread());
                // scheduleddPool.execute(new MyThread());
                // stealingdPool.execute(new MyThread());
                // executor.execute(new MyThread());
                Future<?> submit = singlePool.submit(new MyThread());
                System.out.println(submit);
            }
        }
    
    }

    3.线程池的拒绝策略

      当请求任务不断的过来,而系统此时又处理不过来的时候,我们需要采取的策略是拒绝服务。RejectedExecutionHandler接口提供了拒绝任务处理的自定义方法的机会。在ThreadPoolExecutor中已经包含四种处理策略。

    1. AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。
    2. CallerRunsPolicy 策略:只要线程池未关闭,该策略直接在调用者线程中,运行当前的被丢弃的任务。
    3. DiscardOleddestPolicy策略: 该策略将丢弃最老的一个请求,也就是即将被执行的任务,并尝试再次提交当前任务。
    4. DiscardPolicy策略:该策略默默的丢弃无法处理的任务,不予任何处理。

      除了JDK默认为什么提供的四种拒绝策略,我们可以根据自己的业务需求去自定义拒绝策略,自定义的方式很简单,直接实现RejectedExecutionHandler接口即可

    比如Spring integration中就有一个自定义的拒绝策略CallerBlocksPolicy,将任务插入到队列中,直到队列中有空闲并插入成功的时候,否则将根据最大等待时间一直阻塞,直到超时。

    4.execute和submit的区别

      execute适用于不需要关注返回值的场景,只需要将线程丢到线程池中去执行就可以了

      submit方法适用于需要关注返回值的场景

    public interface ExecutorService extends Executor {
      ...
      <T> Future<T> submit(Callable<T> task);
      <T> Future<T> submit(Runnable task, T result);
      Future<?> submit(Runnable task);
      ...
    }

      其子类AbstractExecutorService实现了submit方法,可以看到无论参数是Callable还是Runnable,最终都会被封装成RunnableFuture,然后再调用execute执行。

    5.线程池的关闭

      关闭线程池可以调用shutdownNow和shutdown两个方法来实现

      shutdownNow:对正在执行的任务全部发出interrupt(),停止执行,对还未开始执行的任务全部取消,并且返回还没开始的任务列表

      shutdown:当我们调用shutdown后,线程池将不再接受新的任务,但也不会去强制终止已经提交或者正在执行中的任务

      还有一些业务场景下需要知道线程池中的任务是否全部执行完成,当我们关闭线程池之后,可以用isTerminated来判断所有的线程是否执行完成,千万不要用isShutdown,isShutdown只是返回你是否调用过shutdown的结果。

    6.自定义线程池

    在实际的使用过程中,大部分我们都是用Executors去创建线程池直接使用,如果有一些其他的需求,比如指定线程池的拒绝策略,阻塞队列的类型,线程名称的前缀等等,我们可以采用自定义线程池的方式来解决。

    如果只是简单的想要改变线程名称的前缀的话可以自定义ThreadFactory来实现,在Executors.new…中有一个ThreadFactory的参数,如果没有指定则用的是DefaultThreadFactory。

    自定义线程池核心在于创建一个ThreadPoolExecutor对象,指定参数

    下面我们看下ThreadPoolExecutor构造函数的定义:

    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler) ;
      • corePoolSize
        线程池大小,决定着新提交的任务是新开线程去执行还是放到任务队列中,也是线程池的最最核心的参数。一般线程池开始时是没有线程的,只有当任务来了并且线程数量小于corePoolSize才会创建线程。

      • maximumPoolSize
        最大线程数,线程池能创建的最大线程数量。

      • keepAliveTime
        在线程数量超过corePoolSize后,多余空闲线程的最大存活时间。

      • unit
        时间单位

      • workQueue
        存放来不及处理的任务的队列,是一个BlockingQueue。

      • threadFactory
        生产线程的工厂类,可以定义线程名,优先级等。

      • handler
        拒绝策略,当任务来不及处理的时候,如何处理, 前面有讲解。

      阿里java编码规范明确说明:禁止使用Executors创建线程池,而应该使用ThreadPollExecutor来创建,并且使用ThreadFactory给出线程名称,以便跟踪。这样做的好处:让创建线程池的开发人员明确线程池运行规则,避免资源耗尽!

      1)FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM(Out Of Memory)。

      2)CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

    所以,明确指名线程池核心线程数、最大线程数,以及队列最大持有任务数,能够更自主控制线程池。

      参考:https://mp.weixin.qq.com/s/5dexEENTqJWXN_17c6Lz6A

         ThreadFactory与BlockingQueue

  • 相关阅读:
    PMP--4.3-1 进度管理计划
    PMP--4.3 规划进度管理&规划进度管理工具与技术--进度管理计划
    PMP--4.2 规划范围管理--范围管理计划--需求管理计划
    PMP工具与技术篇--4.2.3 WBS分解
    PMP--4.2.3-1 范围基准**--WBS词典
    PMP --4.2.3 创建WBS(工作分解结构)--WBS--范围基准
    PMP工具与技术篇--4.2.2 定义范围工具与技术总结--产品分析
    PMP--4.2.2 定义范围--项目范围说明书
    PMP工具与技术篇--4.2.1-6 原型(DEMO)法
    PMP工具与技术篇--4.2.1-5 观察与交谈(人际关系与团队技能)
  • 原文地址:https://www.cnblogs.com/soul-wonder/p/9290860.html
Copyright © 2011-2022 走看看