目录
前言
本文章是在学习过程中的问答整理与总结,不定时更新,仅供参考。
一、线程池
1.常见的线程池有哪些?
1.FixedThreadPool(固定容量线程池)
特点:核心线程数等于最大线程数,即该线程池只能创建核心线程。KeepAliveTime为0,线程执行完后立即回收。任务队列为LinkedBlockingQueue。
2.SingleThreadExecutor(单线程线程池)
特点:只有一个核心线程,线程执行完任务直接回收。任务队列为LinkedBlockingQueue。
3.ScheduledThreadPool(定时线程池)
特点:指定核心线程数,普通线程数量无限,线程执行完任务立即回收,任务队列为延时阻塞队列,适合执行定时或周期性的任务。
4.CachedThreadPool(缓存线程池)
特点:没有核心线程,线程闲置60s后回收,任务队列使用SynchronousQueue这种无容量的同步队列,适用于任务量大,但耗时低的场景。
2.线程池有哪些参数?
1.corePoolSize 核心线程数
池中一直保持存活的线程数,即使这些线程处于空闲。但是将 allowCoreThreadTimeOut 设置成true 后,核心线程处于空闲一段时间以上,也会被回收。
2.maximumPoolSize 最大线程数
当核心线程全部繁忙,且任务队列全部打满之后,线程池会临时追加线程,直到总线程数达到maximumPoolSize 这个上限
3.keepAliveTime 线程空闲超时时间
当非核心线程处于空闲状态的时间超过这个时间后,该线程将被回收。当allowCoreThreadTimeOut被设置成true之后,核心线程也会被回收。
4.unit 单位
keepAliveTime 的单位。天、时、分、秒、毫秒、微秒、纳秒
5.workQueue 任务队列
采用阻塞队列实现,当核心线程全部繁忙之后,后续由execute方法提交的Runnable将存放在任务队列中,等待被线程处理。
6.threadFactory 线程工厂
指定线程池创建线程的方式。
7.handler 拒绝策略
当线程池中的线程数达到 maxiMumPoolSize,且workQueue全部打满之后,后续提交的任务将被拒绝,handler可以指定用什么方法拒绝。
3.核心线程数和最大线程数有什么区别?
当线程池接收到一个任务时,首先会判断核心线程数是否已满,如果未满,则会创建一个线程去执行该任务,如果已满,则会判断任务队列是否已满,如果未满,则会将任务添加到队列中,如果已满,则会判断最大线程数是否已满,如果未满,则会创建一个普通线程去执行该任务,如果已满,则会采用拒绝策略拒绝该任务
4.任务队列有哪些?
1.SynchronousQueue 同步队列
这是一个内部没有任何容量的阻塞队列,任何一次的插入操作的元素都需要等待相对的删除/读取操作,否则进行插入操作的线程就要一直等待。
2.LinkedBlockingQueue 无界队列
基于链表结构,使用无界队列后,当核心线程繁忙时,后续任务可以无限地加入队列,因此,线程池中的线程数不会超过核心线程数。这种队列可以提高线程池的吞吐量,但代价是牺牲内存空间,甚至导致内存溢出,另外可以为它指定容量,这样它就变成一个有界队列了。
3.ArrayBlockingQueue 有界队列
基于数组结构,在线程池初始化时,指定队列的容量,后续无法再调整,这样可以有效防止资源耗尽,但可能更难调整和控制。
4.PriorityBlockingQueue 支持优先级排序的无界阻塞队列
存放在PriorityBlockingQueue中的元素必须实现Comparable接口,这样才能通过compareTo() 方法进行排序,优先级最高的元素始终排在列头,但不保证优先级一样的元素的排序,也不保证除优先级最高外的元素,始终处于正确的位置。
5.DelayQueue 延迟队列
基于二叉堆实现,同时具备:无界队列、阻塞队列、优先队列的特点,存放的对象必须实现Delayed接口,通过执行时延从队列中提取任务,时间没到任务取不出。
6.LinkedBlockingDeque 双端队列
既可以从尾部插入/取出元素,也可以从头部插入/取出元素。
7.LinkedTransferQueue 由链表结构组成的无界阻塞队列
采用预占模式,消费者线程取元素时,如果队列不为空,直接取走元素,如果队列为空,就插入一个元素为null的节点,然后消费者线程被等待在这个线程上,后面生产者线程入队时,发现有一个为null的节点,就直接将元素填充到该节点,并唤醒在该节点等待的消费者线程,取走元素。
5.拒绝策略有哪些?
1.AbortPolicy(默认)
丢弃任务并抛出 RejectedExecutionException 异常
2.CallerRunsPolicy
直接运行这个任务的run方法,但并非是由线程池的线程运行,而是交由任务的调用线程处理。
3.DiscardPolicy
直接丢弃任务,不抛出异常
4.DiscardOldestPolicy
将处于等待队列列头的等待任务强行取出,然后再尝试将被拒绝的任务提交到线程池执行
6.Executor和Executors有什么区别
Executor 接口对象能执行我们的线程任务。
Executors 工具类的不同方法能按我们的需求创建不同的线程池,来满足业务需求。
ExecutorService 接口继承了Executor接口,并进行了扩展,提供了更多的方法,我们能够获得任务执行的状态,并可以获取任务的返回值。
使用ThreadPoolExecutor 可以创建自定义线程池。
Future 表示异步计算的结果,他提供了检查计算是否完成的方法,以等待计算的完成,并可以使用get()方法获取计算的结果。
7.什么是Executors框架
Executors 框架是一个可以根据一组执行策略调用、调度、执行和控制的异步任务框架。
无限制地创建线程会引起应用程序内存溢出,所以创建一个线程池是个更好地解决方案,因为可以限制线程的数量,并且可以回收再利用这些线程。利用Executors 框架可以非常方便地创建一个线程池。
8.什么是线程组,为什么不推荐在java中使用
线程组和线程池是两个不同的概念,线程组是为了方便线程的管理,而线程池是为了管理线程的生命周期,复用线程,减少创建销毁线程的开销。
9.为什么使用Executors框架,比使用应用创建和管理线程好
1.使用new Thread() 创建线程比较消耗性能,且创建的线程缺乏管理。
2.无限制地创建线程,线程之间的相互竞争会导致过多占用系统资源导致系统瘫痪。
3.线程之间的频繁交替也会消耗很多资源。
4.直接使用new Thread() 启动的线程不利于扩展,比如:定时执行、定期执行、定时定期执行、线程中断等都不易于实现。
使用Executors框架则具有以下优点
1.复用已存在且空闲的线程从而减少线程对象的创建与销毁造成的开销
2.可有效控制最大并发线程数,提高系统资源的利用率,同时避免过多资源竞争
3.框架中有线程的定时、定期、单线程、并发数控制等功能
10.什么是线程池,为什么要用它
创建线程需要花费昂贵的资源和时间,如果任务来了才创建线程,那么响应时间会变长,而且一个进程能创建的线程数量有限。为避免这些问题,在程序启动时就创建若干线程来响应处理,他们被成为线程池,里面的线程叫做工作线程。从jdk1.5开始,Java API 提供了Executor框架让你可以创建不同的线程池。
11.java线程池中submit()和execute()方法有什么区别
两种方法都可以向线程池提交任务,execute() 方法的返回类型是 void(),它定义在 Executor 接口中。而 submit() 方法可以持有计算结果的Future对象,它定义在 ExecutorService 接口中,它扩展了 Executor 接口,其他线程池类像 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 都有这些方法。
12.线程池的大小如何设置
任务为cpu密集型任务时,线程池大小设置为N+1,N为CPU核心数,比核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其他原因导致任务暂停带来的影响。一旦任务暂停,CPU就会处于空闲状态,多出来的一个线程就可以充分利用CPU的空闲时间。
任务为I/O密集型任务时,线程池大小设置为2N,这种任务应用起来,系统会用大部分时间来处理I/O交互,而线程在处理I/O交互时,不会占用CPU资源,这时就可以将CPU交出给其他线程使用,因此我们可以多配置一些线程。
13.给用户发消息任务超出队列,用哪个拒绝策略,有其他的方法吗
会使用CallerRunsPolicy,还可以使用无界队列,继续添加任务到阻塞队列中等待;或者用消息队列存任务数据,等待线程池慢慢处理。
总结
本文所写内容由翻阅整理所得,不定时更新,仅供参考。