java线程池——面试常问
2021/4/19 20:25:29
本文主要是介绍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
- 该状态表示线程池彻底终止
这篇关于java线程池——面试常问的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-26手写消息中间件:从零开始的指南
- 2024-11-26Java语音识别项目资料:新手入门教程
- 2024-11-26JAVA语音识别项目资料:新手入门教程
- 2024-11-26Java语音识别项目资料:入门与实践指南
- 2024-11-26Java云原生资料入门教程
- 2024-11-26Java云原生资料入门教程
- 2024-11-26Java云原生资料:新手入门教程
- 2024-11-25Java创意资料:新手入门的创意学习指南
- 2024-11-25JAVA对接阿里云智能语音服务资料详解:新手入门指南
- 2024-11-25Java对接阿里云智能语音服务资料详解