自学内容网 自学内容网

Android 线程池的面试题 线程线程池面试题

1.为什么要用线程池
降低资源消耗:通过复用线程,降低创建和销毁线程的损耗。
提高响应速度:任务不需要等待线程创建就能立即执行。
提高线程的可管理性:使用线程池可以进行统一的分配、调优和监控。
2. 线程池执行流程(原理)
ThreadPoolExecutor的执行流程(原理)
如果核心线程池没满,就创建新线程来处理任务。
如果核心线程池满了,就将任务加入到阻塞队列。
如果阻塞队列满了,就创建临时线程来处理任务。
如果线程池满了,任务将被拒绝,并按照拒绝策略来处理。
  模拟 10 个任务,配置的核心线程数为 5 、等待队列容量为 100 ,所以每次只可能存在 5 个任务同时执行,剩下的 5 个任务会被放到等待队列中。当前的 5 个任务执行完成后,才会执行剩下的 5 个任务。

面试题: 现有一个线程池,参数corePoolSize = 5,maximumPoolSize = 10,BlockingQueue阻塞队列长度为 5 :

Q:有4个任务同时进来,问:线程池会创建几条线程?

A:执行execute方法会新建 4 条线程来执行任务;

Q:又同时进来 2 个任务呢(总共6个任务了)?

A:会新建 1 条线程来执行任务,这时核心线程池满了,但还剩下1个任务,线程池会将剩下这个任务放进阻塞队列中,等待空闲线程执行。

Q:又同时进来了 5 个任务呢(总共11个任务了)?

A:线程池会继续将这5个任务放进阻塞队列,然后导致阻塞队列满了,此时核心线程也用完了,但还剩下1个任务,于是线程池会创建 1 条“临时”线程来执行这个任务。“临时”指的是他们不会长期存在于线程池,存活时间为 keepAliveTime。

3.有哪些线程池?/ 有哪几种方法创建线程池?(常见的线程池,线程池的种类,线程池的实现类,阻塞队列,缺点)
方式一:通过Executor 框架的工具类Executors来实现:

Executors.newFixedThreadPool()方法返回一个线程数量固定的线程池。超过这个数量后新进来的任务会被放到等待队列中,等线程空闲时,再按先进先出的原则处理队列中的任务。
Executors.newSingleThreadExecutor()方法返回一个只有一个线程的线程池。超过这个数量后新进来的任务会被放到等待队列中,等线程空闲时,再按先进先出的原则处理队列中的任务。
  FixedThreadPool 和 SingleThreadExecutor 使用的都是 LinkedBlockingQueue 作为等待队列,这是一个无界队列,允许请求的队列长度为 Integer.MAX_VALUE ,所以可能堆积大量请求,导致OOM。

Executors.newCachedThreadPool()方法返回一个可根据实际情况调整线程数量的线程池。在核心线程达到最大值之前,有任务进来就会创建新的核心线程,并加入核心线程池。达到最大核心线程数后,新任务进来,如果有空闲线程,就直接拿来使用,如果没有,就放到等待队列中来处理。它使用的是 SynchronousQueue 作为等待队列,所以不会保留任务,直接创建临时线程来处理新任务,允许创建的线程数量为 Integer.MAX_VALUE,所以可能会创建大量线程,导致OOM。
方式二:通过构造方法 ThreadPoolExecutor 自定义参数来实现。
  

4. 线程池需要输入的参数
corePoolSize:表示核心线程池的大小。当提交一个任务时,如果当前线程池的线程个数没有达到corePoolSize,即使有空闲线程,也会创建新的线程来执行所提交的任务。
maximumPoolSize:表示线程池允许创建的最大线程数。如果阻塞队列已满,并且已创建的线程数小于最大线程数,则线程池会创建新的线程执行任务。
workQueue:表示阻塞队列,用于保存等待执行的任务。常用的阻塞队列有:①基于数组结构的有界阻塞队列 ArrayBlockingQueue。②基于链表结构的阻塞队列 LinkedBlockingQueue。③不存储元素的阻塞队列 SynchronousQueue。④具有优先级的阻塞队列 PriorityBlockingQueue。 阻塞队列
keepAliveTime:当线程池中的线程数量大于 corePoolSize 时,如果没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime 才会被回收销毁。
TimeUnit:表示keepAliveTime的时间单位。
handler:饱和策略(拒绝策略)。当线程池的阻塞队列已满并且线程数量达到了最大线程数,就需要采用拒绝策略来处理新任务。线程池提供了四种拒绝策略:
①AbortPolicy,表示直接抛出异常拒绝处理新任务,它也是默认情况下的策略。
②CallerRunsPolicy:表示用调用者所在的线程来运行任务。如果不能丢弃任何一个任务,就选用这个策略。
③DiscardPolicy:表示丢弃掉无法处理的任务。如果允许任务丢失,这是最好的策略方案。
④DiscardOldestPolicy:表示丢弃掉队列里最旧的未处理的任务。
ThreadFactory:用于设置创建线程的工厂。(可以在自定义的ThreadFactory内将所有线程都设置为守护线程,这样当主线程退出后,会销毁线程池。)
5.线程池中一般设置多少线程?线程池大小如何设置?
(在京东实习中接触的项目,对于线程池参数设置的是核心线程数为5,最大线程数设置的是10,队列大小为1000,因为cpu核数是4,按照经验应该设置为2倍的cpu核数+1,所以应该是9,我们于是设置为10了;队列大小之所以设为1000,是因为我们处理的批次是100,设为1000的话可以放十个批次,而我们的系统处理速度其实是很快的)
对于CPU密集型任务(N+1):尽量使用较小的线程池,一般设置线程池大小为CPU核心数+1。 因为CPU密集型任务使得CPU使用率很高,若开过多的线程,只会增加上下文切换的次数,带来额外的开销。
对于IO密集型任务:由于这类任务对CPU的使用率不高,所以可以设置较大点的线程池,让CPU在等待IO的时候去处理别的任务,充分利用CPU的资源。一般设置为 CPU核心数 乘以 CPU使用率 再乘以 线程等待时间与运行时间的比例加1 得到的值,也就是线程等待时间所占比例越高,就设置越多的线程;线程运行时间所占比例越高,就设置越少的线程。
  CPU核心数 x CPU使用率 x [(线程等待时间 / 线程运行时间) + 1]
(如果线程池设置的太大,大量线程可能会同时争取CPU资源,导致产生大量的上下文切换,而且管理这么多线程,开销也会增大。如果设置太小,同一时间有大量任务需要处理时,就会导致任务在队列中等待执行,大量任务堆积会导致 OOM)
6. 什么是上下文切换?
  当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换。

7.任务队列满了以后再来一个任务如何处理?
  判断线程池运行的线程数量是否达到了最大线程数量。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则执行拒绝策略。
  

8.如何向线程池提交任务?(execute 方法和 submit 方法的区别)
execute 方法没有返回值,所以无法判断任务是否被线程池执行成功。
submit 方法有返回值,返回一个Future类型的对象,通过这个Future对象可以判断任务是否执行成功。
-----------------------------------
Android 线程池的面试题 线程线程池面试题
https://blog.51cto.com/u_16213577/8724434


原文地址:https://blog.csdn.net/qq_33209777/article/details/140627251

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!