zoukankan      html  css  js  c++  java
  • 线程池的问题

    一、什么是线程池?

      一个线程池管理一组工作的线程,同时它还包括一个用于放置等待执行任务的任务队列(阻塞队列)。

    二、线程池的工作原理

      默认情况下,在创建线程池后,线程池中的线程数为0,当任务提交给线程池后处理策略如下:

      1. 如果线程池中的数量小于 corePoolSize(核心线程池大小),即使线程池中的线程都处于空闲状态,也要创建一个新的线程来处理被添加的任务。

      2. 如果线程池中的数量大于等于corePoolSize,但是缓冲队列 WorkQueue 未满,那么任务被放入到缓冲队列,则该任务会等待空闲线程将其去出去执行。

      3. 如果线程池中的数量大于等于corePoolSize,而且缓冲队列 WorkQueue 已满,并且线程池中的数量小于最大线程数 MaximumPoolSize,则建新的线程来处理被添加的任务。

      4. 如果线程池中的数量大于等于corePoolSize,而且缓冲队列 WorkQueue 已满,并且线程池中的数量等于最大线程数 MaximumPoolSize,则通过RejectedExceptionHandler所指定的策略(任务拒绝策略)来处理此任务。

      即处理任务的优先级为:corePoolSize -> WorkQueue -> MaximumPoolSize,如果3这都满,则交给 RejectedExceptionHandler 处理被拒绝的任务。

      5. 特别注意:在 corePoolSize 和 MaximunPoolSize 之间的线程会被自动释放。当线程数量大于corePoolSize时,如果某个线程空间超过KeepAliveTime,线程将被终止,直至线程池中线程数量不大于corePoolSize。这样,线程池可以动态调节池中线程的数量。

    三、使用线程池的好处

      1. 通过重复利用已创建的线程,减少在创建和销毁线程上所花的时间以及系统资源的开销。

      2. 提高相应速度。当任务到达时,任务可以不需要等到线程创建就可以执行。

      3. 提高线程的可管理性。使用线程池可以对线程统一的分配和监控。

      4. 如果不使用线程池,系统会创建大量的线程导致消耗完系统内存。

    四、线程池的创建

    1. 接口与抽象类

      Executors 类(推荐)

      Executor 接口

      ExecutorService 接口

      AbstractExecutorService 抽象类

      ThreadPoolExecutor 类(不推荐)

     继承关系:Executor <- ExecutorService <- AbstractExecutorService <- ThreadPoolExecutor

    2. Executors 类:(推荐使用)
      它主要用来创建线程池。
      Executors.newSingleThreadExecutor(); //创建容量为1 的线程池
      Executors.newFixedThreadPool(int n); //创建固定容量大小的线程池
      Executors.newCachedThreadPool(); //创建一个线程池,线程池最大容量为 Integer.MAX_VALUE(无界线程池)

     1 public static void testExecutor() {
     2         //ExecutorService service = Executors.newSingleThreadExecutor();
     3         ExecutorService service = Executors.newFixedThreadPool(2);//创建两个线程
     4         service.submit(new Runnable() {
     5             @Override
     6             public void run() {
     7                 for (int i = 0; i < 10; ++i) {
     8                     try {
     9                         Thread.sleep(1000);
    10                         System.out.println("Executor1:" + i);
    11                     } catch (Exception e) {
    12                         e.printStackTrace();
    13                     }
    14                 }
    15             }
    16         });
    17 
    18         service.submit(new Runnable() {
    19             @Override
    20             public void run() {
    21                 for (int i = 0; i < 10; ++i) {
    22                     try {
    23                         Thread.sleep(1000);
    24                         System.out.println("Executor2:" + i);
    25                     } catch (Exception e) {
    26                         e.printStackTrace();
    27                     }
    28                 }
    29             }
    30         });
    31 
    32         service.shutdown();
    33         while (!service.isTerminated()) {
    34             try {
    35                 Thread.sleep(1000);
    36                 System.out.println("Wait for termination.");
    37             } catch (Exception e) {
    38                 e.printStackTrace();
    39             }
    40         }
    41     }

    3. ThreadPoolExecutor 类

    1 public ThreadPoolExecutor(int corePoolSize,
    2                               int maximumPoolSize,
    3                               long keepAliveTime,
    4                               TimeUnit unit,
    5                               BlockingQueue<Runnable> workQueue,
    6                               RejectedExecutionHandler handler);

      corePoolSize:核心线程数。

      maximunPoolSize:线程池最大线程数

      keepAliveTime:默认情况下,线程池线程数大于 corePoolSize 后,keepAliveTime 才起作用,如果一个线程空闲时间达到 keepAliveTIme,则会终止,直到线程数不超过 corePoolSize

      unit:参数keepAliveTime的 的时间参数

      workQueue:阻塞队列,任务缓存队列,存放等待执行的任务

        workQueue 的类型为BlockingQueue接口 ,有三种实现类型:

        1)ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;

        2)LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;

        3)synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。在某次添加元素后必须等待其他线程取走后才能继续添加

      ThreadFactor:线程工厂,用来创建线程

      handler:表示当拒绝任务时的策略,有以下取值:

        1.AbortPolicy:直接抛出异常(默认的)

        2.DiscardPolicy:直接丢弃任务

        3.DiscardOldestPolicy:丢弃队列中最旧(队头)的任务,并执行当前任务

        4.CallerRunsPolicy:不用线程池中的线程执行,用调用者所在线程执行

    ThreadPoolExecutor 有以下几个非常重要的方法:

      execute()

      submit()

      shutdown()

      shutdownNow()

    execute() 与 submit() 的区别:

      1. submit 有返回值,execute 没有返回值。所以说可以根据任务有无返回值选择对应的方法。  

      2.  submit 方便异常的处理。如果任务可能会抛出异常,而且希望外面的调用者能够感知这些异常,那么就需要调用submit 方法,通过捕获Future.get抛出的异常。

    shutdown() 与 shutdownNow() 的区别:

      1.都是用来关闭线程池的。

      2. shutdown 方法:此方法执行后不得向线程池再提交任务,如果有空闲线程则销毁空闲线程,等待所有正在执行的任务及位于阻塞队列中的任务执行结束,然后销毁所有线程。

      3. shutdownNow 方法:此方法执行后不得向线程池再提交任务,如果有空闲线程则销毁空闲线程,取消所有位于阻塞队列中的任务,并将其放入List<Runnable>容器,作为返回值。取消正在执行的线程(实际上仅仅是设置正在执行线程的中断标志位,调用线程的interrupt 方法来中断线程)。

  • 相关阅读:
    深入理解计算机系统(3.2)---数据格式、访问信息以及操作数指示符
    深入理解计算机系统(3.1)---走进汇编的世界
    linux下的usb抓包方法
    Linux中查看系统资源占用情况的命令
    中药介绍
    shell中的for循环用法详解
    Linux上shell脚本date的用法
    linux下的shell运算(加、减、乘、除)
    [linux time命令学习篇] time 统计命令执行的时间
    一文简述多种强化学习算法,重要概念和术语一览
  • 原文地址:https://www.cnblogs.com/yumingxing/p/9555919.html
Copyright © 2011-2022 走看看