java并发 - 线程池

80 阅读3分钟

前言

7题。

线程池的执行流程?

当我们提交一个任务到线程池中,线程池的处理流程大致如下:

  1. 首先判断线程池里的核心线程数是否已满,如果没满,则创建一个新的核心线程来执行任务;

  2. 如果核心线程满了,则判断工作队列是否已满,如果没满,则将新提交的任务存储在这个工作队列里;

  3. 如果工作队列满了,则判断最大线程数是否已满,如果没满,则创建临时线程直接执行任务;

  4. 如果最大线程数满了,则会执行对应的拒绝策略。

线程池的核心参数?

线程池在创建的时候最大支持传入7个参数,分别是:

  1. 核心线程数;

  2. 最大线程数;

  3. 临时线程存活时间;

  4. 临时线程的存活的时间单位(秒、分、时、天);

  5. 工作队列:用来保存等待执行的任务的;

  6. ThreadFactory:设置创建线程的工厂;

  7. Handler:线程池的拒绝策略。

线程池的拒绝策略有哪些?

拒绝策略是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略,官方提供的有4种:

  1. AbortPolicy:直接抛出异常,默认策略;

  2. CallerRunsPolicy:用调用者所在的线程来执行任务;

  3. DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;

  4. DiscardPolicy:直接丢弃任务。

线程池的阻塞队列有哪些?

阻塞队列指的是当线程中核心线程数已满,新任务到达时,存储线程的队列,常见的有下面几种:

  1. ArrayBlockingQueue:基于数组结构的有界阻塞队列;

  2. LinkedBlockingQueue:基于链表结构的有界阻塞队列;

  3. PriorityBlockingQueue:具有优先级别的阻塞队列;

  4. SynchronousQueue:不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作。

submit和execute方法的区别?

都是线程池中用于提交线程任务的方法,区别点在于:

  1. 两个方法的参数都可以是Runnable对象;submit方法的参数还可以是Callable对象。

  2. submit可以返回一个Future对象,从这个对象中可以拿到线程运行结果,execute没有返回值。

  3. submit能够控制异常,在主线程中通过Futureget方法捕获线程中的异常;execute不可以。

了解Executors创建线程池吗?

ExcutorsJDK提供的一个可以创建线程池的工具类,可以直接创建出四种:

  1. 创建单线程化的线程池;

  2. 创建一个定长线程池;

  3. 创建一个可缓存的线程池;

  4. 创建固定大小的线程,延迟或定时执行任务。

但是用它创建的线程池有的没有限制最大线程数,有的没有限制阻塞队列的长度,这样的话,极大可能导致OOM(内存用完了),因此实际不常用,大部分时候还是使用自己传递参数的方式创建线程池。

如何确定线程池的核心线程数和最大线程数?

有两种情况,任务可能是CPU密集型(大部分时间都在进行运算)或IO密集型(大部分时间都在等待I/O操作):

  1. 如果是CPU密集型,核心线程数可以设置为CPU核数+1

  2. 如果是IO密集型,核心线程数可以设置为CPU核数的2倍

最大线程数与核心线程数相同或稍大