专注Java教育14年 全国咨询/投诉热线:400-8080-105
动力节点LOGO图
始于2009,口口相传的Java黄埔军校
首页 学习攻略 Java学习 程序员常见的Java线程池面试题

程序员常见的Java线程池面试题

更新时间:2020-03-11 10:20:35 来源:动力节点 浏览5568次

Java通过Executors提供四种线程池

  • CachedThreadPool():可缓存线程池。
  • FixedThreadPool():定长线程池。
  • ScheduledThreadPool():定时线程池。
  • SingleThreadExecutor():单线程化的线程池。

ThreadPoolExecutor的执行流程

  • 线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务。
  • 线程数量达到了corePools,则将任务移入队列等待。
  • 队列已满,新建线程(非核心线程)执行任务。
  • 队列已满,总线程数又达到了maximumPoolSize,就会由(RejectedExecutionHandler)抛出异常(拒绝策略)
  • 新建线程->达到核心数->加入队列->新建线程(非核心)->达到最大数->触发拒绝策略

ThreadPoolExecutor的几个参数

corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。

maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize;当阻塞队列是无界队列,则maximumPoolSize不起作用,因为无法提交至核心线程池的线程会一直持续地放入workQueue(工作队列)中。

keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0。

allowCoreThreadTimeout:默认情况下超过keepAliveTime的时候,核心线程不会退出,可通过将该参数设置为true,让核心线程也退出。

unit:可以指定keepAliveTime的时间单位。

workQueue

ArrayBlockingQueue有界队列,需要指定队列大小。

LinkedBlockingQueue若指定大小则和ArrayBlockingQueue类似,若不指定大小则默认能存储Integer.MAX_VALUE个任务,相当于无界队列,此时maximumPoolSize值其实是无意义的。

SynchronousQueue同步阻塞队列,当有任务添加进来后,必须有线程从队列中取出,当前线程才会被释放,newCachedThreadPool就使用这种队列。

RejectedExecutionHandler:线程数和队列都满的情况下,线程池会执行的拒绝策略,有四个(也可以使用自定义的策略)。

线程池的四种拒绝策略

  • AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满,线程池默认策略。
  • DiscardPolicy:不执行新任务,也不抛出异常,基本上为静默模式。
  • DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行。
  • CallerRunPolicy:拒绝新任务进入,如果该线程池还没被关闭,那么这个新的任务在执行线程中被调用。

Executors和ThreadPoolExecutor创建线程的区别

Executors

newFixedThreadPool和newSingleThreadExecutor:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。

newCachedThreadPool和newScheduledThreadPool:主要问题是线程数最大数是Integer.MAX_VALUE(2的31次方-1,int类型最大值),可能会创建数量非常多的线程,甚至OOM。

ThreadPoolExecutor

创建线程池方式只有一种,就是走它的构造函数,参数自己指定。

为什么使用线程池

减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

运用线程池能有效的控制线程最大并发数,可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

对线程进行一些简单的管理,比如:延时执行、定时循环执行的策略等,运用线程池都能进行很好的实现。

如何向线程池中提交任务

可以通过execute()或submit()两个方法向线程池提交任务。

execute()方法没有返回值,所以无法判断任务知否被线程池执行成功。

submit()方法返回一个future,那么我们可以通过这个future来判断任务是否执行成功,通过future的get方法来获取返回值。

如何关闭线程池

可以通过shutdown()或shutdownNow()方法来关闭线程池。

shutdown的原理是只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。

shutdownNow的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。shutdownNow会首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。

程序员常见的Java线程池面试题

以上就是动力节点Java培训机构小编介绍的“程序员常见的Java线程池面试题”的内容,希望对大家有帮助,如有疑问,请在线咨询,有专业老师随时为你服务。

提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>