java线程池——面试常问
线程池面试常问
参考blog
https://zhuanlan.zhihu.com/p/73990200
java线程池 最大线程和核心线程
线程池的优势
1. 它帮我们管理线程,避免增加创建线程和销毁线程的资源损耗。因为线程其实也是一个对象,创建一个对象,需要经过类加载过程,销毁一个对象,需要走GC垃圾回收流程,都是需要资源开销的。
2. 提高响应速度。 如果任务到达了,相对于从线程池拿线程,重新去创建一条线程执行,速度肯定慢很多。
3. 重复利用。 线程用完,再放回池子,可以达到重复利用的效果,节省资源。
最大线程数:就是线程池里面能够容纳的最大线程容量
核心线程:我的理解就是 线程池中常驻线程,不会被销毁的线程。主要区别于 非核心线程
Java的线程池说一下,各个参数的作用,如何进行的?
// java中的线程池是由ThreadPoolExecutor实现的
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
-
参数含义
- corePoolSize 核心线程数
- maximumPoolSize 最大线程数
- long keepAliveTime 线程池中非核心线程空闲的存活时间
- TimeUnit unit 空闲时间单位
- BlockingQueue
workQueue 任务的阻塞队列 - ThreadFactory threadFactory 用于创建线程的工厂
- RejectedExecutionHandler handler 线程池饱和之后的策略。主要是四种
-
线程池执行流程————调用executor()方法
- 首先看核心线程是否已经全部在工作。如果没有,交给核心线程执行。反之进行下一步
- 如果任务队列没有满,就将任务放到任务队列中阻塞。如果任务队列满了,进行下一步。
- 如果线程数没有超过最大线程数,那么可以创建 非核心线程 来执行任务。如果线程数 超过了 最大线程数,执行下一步
- 根据 RejectedExecutionHandler的类型采取拒绝策略
-
四种拒绝策略
AbortPolicy(抛出一个异常,默认的)
DiscardPolicy(直接丢弃任务)
DiscardOldestPolicy(丢弃队列里最老的任务,将当前这个任务继续提交给线程池)
CallerRunsPolicy(交给线程池调用所在的线程进行处理)
线程池异常处理
在使用线程池处理任务的时候,任务代码可能抛出RuntimeException,抛出异常后,线程池可能捕获它,也可能创建一个新的线程来代替异常的线程,我们可能无法感知任务出现了异常,因此我们需要考虑线程池异常情况。
四种方法:
1. 利用try-catch捕获异常
2. submit()执行,Future.get()接受异常
3. 重写 ThreadPoolExecutor的afterExecute()方法,处理传递的异常引用
4. 传入自己的ThreadFactory,设置Thread.UncaughtExceptionHandler来处理异常
线程池都有哪几种工作队列?
-
ArrayBlockingQueue
- 有界队列,是一个用数组实现的有界阻塞队列。 按FIFO排序
-
LinkedBlockingQueue
- 链表实现的阻塞队列。可以指定容量。如果不指定那么就是无界队列,最大长度为Integer.MAX_VALUE,吞吐量通常要高于ArrayBlockingQuene;newFixedThreadPool线程池使用了这个队列
-
DelayQueue(延迟队列)
- 任务定时周期的执行的延迟队列。根据指定执行时间的从小到大排序,否则根据插入到队列的顺序排序。newScheduledThreadPool线程池使用这个。
-
PriorityBlockingQueue
- 优先级队列。具有优先级的无界阻塞队列
-
SynchronousQueue
- 同步队列。一个不存储元素的队列。一个元素的插入必须等到另一个线程的移出操作,否则插入一直处于阻塞状态。newCachedThreadPool线程池使用了这个队列。
几种线程池
- newFixedThreadPool-----------固定线程池
- 核心线程数 = 最大线程数。 空闲时间为0。 使用LinkedBlockingQueue的无界队列
- 执行过程
- 提交任务。看是否有核心线程空闲,如果有直接执行。如果没有的话,加入到阻塞队列中。
- 存在的问题:因为任务队列是无界的Integer.MAX_VALUE 可能会存在OOM(out of memory)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- newSingleThreadPool----------单一线程池
- 核心线程数 = 最大线程数 = 1. 使用LinkedBlockingQueue 无界队列
- 执行过程
- 提交任务。核心线程有空那就执行。没空就到阻塞队列中。
- 存在问题。可能OOM
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- newCachedThreadPool--------缓存线程池
- 核心线程数 = 0, 最大线程数 = Integer.MAX_VALUE。 空闲时间60s。使用SynchronousQueue
- 执行过程
- 提交任务。如果有线程空闲,就直接交给空闲线程执行。如果没有的话创建线程。
SynchronousQueue的容量为0, 是不能够有任务阻塞在里面的
- 提交任务。如果有线程空闲,就直接交给空闲线程执行。如果没有的话创建线程。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- newScheduledThreadPool--------定期线程池
- 使用的是 DelayedWorkQueue延时队列. 最大线程数是 Integer.MAX_VALUE
- 执行过程
添加一个任务
线程池中的线程从 DelayQueue 中取任务
线程从 DelayQueue 中获取 time 大于等于当前时间的task
执行完后修改这个 task 的 time 为下次被执行的时间
这个 task 放回DelayQueue队列中
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
线程池状态
//线程池状态
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
-
RUNNING
- 该状态的线程池会接收到任务。并处理阻塞队列中的任务
- 如果调用 shutdown()方法就切换到 SHUTDOWN()状态
- 如果调用 shutdownNow()方法就切换到STOP()状态
-
SHUTDOWN
- 该状态下的线程池不会接受新的任务。但是会处理阻塞队列中的任务
- 当阻塞队列为空,以及线程池中执行的任务也为空的时候转换为TIDYING状态
-
STOP
- 该状态下的线程池不会接受新的任务。也不处理阻塞队列中的任务
- 线程池中执行的任务为空,进入TIDYING状态;
-
TIDYING
- 该状态表示所有的任务已经终止。记录任务数为0
- 调用teminated()方法,执行完毕,进入TERMINATED状态
-
TERMINATED
- 该状态表示线程池彻底终止