最近,一名读者去蚂蚁金服面试,面试官问了他关于乐观锁和悲观锁的问题,幸亏他看了我的【】文章,结果是替这名读者高兴!现就部分面试题目总结成文,供小伙伴们参考。
小伙伴们可以关注 冰河技术 微信公众号来学习【高并发专题】,学习超硬核知识技能,跳槽大厂,升级加薪,指日可待!
面试汇总 Java中的线程池是如何实现的?在Java中,所谓的线程池中的“线程”,其实是被抽象为了一个静态内部类Worker,它基于AQS实现,存放在线程池的HashSetworkers成员变量中;而需要执行的任务则存放在成员变量workQueue(BlockingQueueworkQueue)中。这样,整个线程池实现的基本思想就是、从workQueue中不断取出需要执行的任务,放在Workers中进行处理。
创建线程池的几个核心构造参数?Java中的线程池的创建其实非常灵活,我们可以通过配置不同的参数,创建出行为不同的线程池,这几个参数包括:
corePoolSize:线程池的核心线程数。
maximumPoolSize:线程池允许的最大线程数。
keepAliveTime:超过核心线程数时闲置线程的存活时间。
workQueue:任务执行前保存任务的队列,保存由execute方法提交的Runnable任务。
线程池中的线程是怎么创建的?是一开始就随着线程池的启动创建好的吗?显然不是的。线程池默认初始化后不启动Worker,等待有请求时才启动。每当我们调用execute()方法添加一个任务时,线程池会做如下判断:
如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;
如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;
如果这时候队列满了,而且正在运行的线程数量小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
如果队列满了,而且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会抛出异常RejectExecutionException。
当一个线程完成任务时,它会从队列中取下一个任务来执行。当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断。如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。
既然提到可以通过配置不同参数创建出不同的线程池,那么Java中默认实现好的线程池又有哪些呢?请比较它们的异同。(1)SingleThreadExecutor线程池这个线程池只有一个核心线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
corePoolSize:1,只有一个核心线程在工作。 maximumPoolSize:1。 keepAliveTime:0L。 workQueue:newLinkedBlockingQueue<Runnable>(),其缓冲队列是无界的。(2)FixedThreadPool线程池
FixedThreadPool是固定大小的线程池,只有核心线程。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。FixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器。
corePoolSize:nThreads maximumPoolSize:nThreads keepAliveTime:0L workQueue:newLinkedBlockingQueue<Runnable>(),其缓冲队列是无界的。(3)CachedThreadPool线程池CachedThreadPool是无界线程池,如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。
线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。SynchronousQueue是一个是缓冲区为1的阻塞队列。
缓存型池子通常用于执行一些生存期很短的异步型任务,因此在一些面向连接的daemon型SERVER中用得不多。但对于生存期短的异步任务,它是Executor的首选。
(4)ScheduledThreadPool线程池ScheduledThreadPool、核心线程池固定,大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。创建一个周期性执行任务的线程池。如果闲置,非核心线程池会在DEFAULT_KEEPALIVEMILLIS时间内回收。
corePoolSize:corePoolSize maximumPoolSize:Integer.MAX_VALUE keepAliveTime:DEFAULT_KEEPALIVE_MILLIS workQueue:newDelayedWorkQueue()5、如何在Java线程池中提交线程?
线程池最常用的提交任务的方法有两种:
execute()、ExecutorService.execute方法接收一个Runable实例,它用来执行一个任务。