【BAT面试题系列,网易Java社招面试题
- 如果外包员工把需求做完了,它经过一段(keepAliveTime)空闲时间,就离开公司了。
好的,到这里。面试问题1->Java的线程池说一下,各个参数的作用,如何进行的? 是否已经迎刃而解啦, 我觉得这个问题,回答:线程池构造函数的corePoolSize,maximumPoolSize等参数,并且能描述清楚线程池的执行流程 就差不多啦。
线程池异常处理
在使用线程池处理任务的时候,任务代码可能抛出RuntimeException,抛出异常后,线程池可能捕获它,也可能创建一个新的线程来代替异常的线程,我们可能无法感知任务出现了异常,因此我们需要考虑线程池异常情况。
当提交新任务时,异常如何处理?
我们先来看一段代码:
ExecutorService threadPool = Executors.newFixedThreadPool(5);for (int i = 0; i < 5; i++) {threadPool.submit(() -> {System.out.println("current thread name" + Thread.currentThread().getName());Object object = null;System.out.print("result## "+object.toString());});}
显然,这段代码会有异常,我们再来看看执行结果
虽然没有结果输出,但是没有抛出异常,所以我们无法感知任务出现了异常,所以需要添加try/catch。 如下图:
OK,线程的异常处理,我们可以直接try…catch捕获。
线程池exec.submit(runnable)的执行流程
通过debug上面有异常的submit方法(建议大家也去debug看一下,图上的每个方法内部是我打断点的地方),处理有异常submit方法的主要执行流程图:
//构造feature对象/*** @throws RejectedExecutionException {@inheritDoc}* @throws NullPointerException {@inheritDoc}*/public Future<?> submit(Runnable task) {if (task == null) throw new NullPointerException();RunnableFuture<Void> ftask = newTaskFor(task, null);execute(ftask);return ftask;}protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {return new FutureTask<T>(runnable, value);}public FutureTask(Runnable runnable, V result) {this.callable = Executors.callable(runnable, result);this.state = NEW; // ensure visibility of callable}public static <T> Callable<T> callable(Runnable task, T result) {if (task == null)throw new NullPointerException();return new RunnableAdapter<T>(task, result);}//线程池执行public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command))reject(command);else if (workerCountOf(recheck) == 0)addWorker(null, false);}else if (!addWorker(command, false))reject(command);}//捕获异常public void run() {if (state != NEW ||!UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()))return;try {Callable<V> c = callable;if (c != null && state == NEW) {V result;boolean ran;try {result = c.call();ran = true;} catch (Throwable ex) {result = null;ran = false;setException(ex);}if (ran)set(result);}} finally {// runner must be non-null until state is settled to// prevent concurrent calls to run()runner = null;// state must be re-read after nulling runner to prevent// leaked interruptsint s = state;if (s >= INTERRUPTING)handlePossibleCancellationInterrupt(s);}
通过以上分析,submit执行的任务,可以通过Future对象的get方法接收抛出的异常,再进行处理。 我们再通过一个demo,看一下Future对象的get方法处理异常的姿势,如下图:
其他两种处理线程池异常方案
除了以上1.在任务代码try/catch捕获异常,2.通过Future对象的get方法接收抛出的异常,再处理两种方案外,还有以上两种方案:
3.为工作者线程设置UncaughtExceptionHandler,在uncaughtException方法中处理异常
我们直接看这样实现的正确姿势:
ExecutorService threadPool = Executors.newFixedThreadPool(1, r -> {Thread t = new Thread(r);t.setUncaughtExceptionHandler((t1, e) -> {System.out.println(t1.getName() + "线程抛出的异常"+e);});return t;});threadPool.execute(()->{Object object = null;System.out.print("result## " + object.toString());});
运行结果:
4.重写ThreadPoolExecutor的afterExecute方法,处理传递的异常引用
这是jdk文档的一个demo:
class ExtendedExecutor extends ThreadPoolExecutor {// 这可是jdk文档里面给的例子。。protected void afterExecute(Runnable r, Throwable t) {super.afterExecute(r, t);if (t == null && r instanceof Future<?>) {try {Object result = ((Future<?>) r).get();} catch (CancellationException ce) {t = ce;} catch (ExecutionException ee) {t = ee.getCause();} catch (InterruptedException ie) {Thread.currentThread().interrupt(); // ignore/reset}}if (t != null)System.out.println(t);}}}
因此,被问到线程池异常处理,如何回答?
线程池的工作队列
线程池都有哪几种工作队列?
ArrayBlockingQueue
LinkedBlockingQueue
DelayQueue
PriorityBlockingQueue
SynchronousQueue
ArrayBlockingQueue
ArrayBlockingQueue(有界队列)是一个用数组实现的有界阻塞队列,按FIFO排序量。
LinkedBlockingQueue
LinkedBlockingQueue(可设置容量队列)基于链表结构的阻塞队列,按FIFO排序任务,容量可以选择进行设置,不设置的话,将是一个无边界的阻塞队列,最大长度为Integer.MAX_VALUE,吞吐量通常要高于ArrayBlockingQuene;newFixedThreadPool线程池使用了这个队列
DelayQueue
DelayQueue(延迟队列)是一个任务定时周期的延迟执行的队列。根据指定的执行时间从小到大排序,否则根据插入到队列的先后排序。newScheduledThreadPool线程池使用了这个队列。
PriorityBlockingQueue
PriorityBlockingQueue(优先级队列)是具有优先级的无界阻塞队列;
SynchronousQueue
SynchronousQueue(同步队列)一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene,newCachedThreadPool线程池使用了这个队列。
针对面试题:线程池都有哪几种工作队列? 我觉得,回答以上几种ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue等,说出它们的特点,并结合使用到对应队列的常用线程池(如newFixedThreadPool线程池使用LinkedBlockingQueue),进行展开阐述, 就可以啦。
几种常用的线程池
newFixedThreadPool (固定数目线程的线程池)
newCachedThreadPool(可缓存线程的线程池)
newSingleThreadExecutor(单线程的线程池)
newScheduledThreadPool(定时及周期执行的线程池)
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory);}
线程池特点:
核心线程数和最大线程数大小一样
没有所谓的非空闲时间,即keepAliveTime为0
阻塞队列为无界队列LinkedBlockingQueue
工作机制:
提交任务
如果线程数少于核心线程,创建核心线程执行任务
如果线程数等于核心线程,把任务添加到LinkedBlockingQueue阻塞队列
如果线程执行完任务,去阻塞队列取任务,继续执行。
实例代码
ExecutorService executor = Executors.newFixedThreadPool(10);for (int i = 0; i < Integer.MAX_VALUE; i++) {executor.execute(()->{try {Thread.sleep(10000);} catch (InterruptedException e) {//do nothing}});
IDE指定JVM参数:-Xmx8m -Xms8m :
run以上代码,会抛出OOM:
因此,面试题:使用无界队列的线程池会导致内存飙升吗?
答案 :会的,newFixedThreadPool使用了无界的阻塞队列LinkedBlockingQueue,如果线程获取一个任务后,任务的执行时间比较长(比如,上面demo设置了10秒),会导致队列的任务越积越多,导致机器内存使用不停飙升, 最终导致OOM。
使用场景
FixedThreadPool 适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务。
newCachedThreadPool
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(),threadFactory);}
线程池特点:
核心线程数为0
最大线程数为Integer.MAX_VALUE
阻塞队列是SynchronousQueue
非核心线程空闲存活时间为60秒
当提交任务的速度大于处理任务的速度时,每次提交一个任务,就必然会创建一个线程。极端情况下会创建过多的线程,耗尽 CPU 和内存资源。由于空闲 60 秒的线程会被终止,长时间保持空闲的 CachedThreadPool 不会占用任何资源。
工作机制
提交任务
因为没有核心线程,所以任务直接加到SynchronousQueue队列。
判断是否有空闲线程,如果有,就去取出任务执行。
如果没有空闲线程,就新建一个线程执行。
执行完任务的线程,还可以存活60秒,如果在这期间,接到任务,可以继续活下去;否则,被销毁。
实例代码
ExecutorService executor = Executors.newCachedThreadPool();for (int i = 0; i < 5; i++) {executor.execute(() -> {System.out.println(Thread.currentThread().getName()+"正在执行");});}
运行结果:
使用场景
用于并发执行大量短期的小任务。
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory));}
线程池特点
核心线程数为1
最大线程数也为1
阻塞队列是LinkedBlockingQueue
keepAliveTime为0
工作机制
提交任务
线程池是否有一条线程在,如果没有,新建线程执行任务
如果有,讲任务加到阻塞队列
当前的唯一线程,从队列取任务,执行完一个,再继续取,一个人(一条线程)夜以继日地干活。
实例代码
ExecutorService executor = Executors.newSingleThreadExecutor();for (int i = 0; i < 5; i++) {executor.execute(() -> {System.out.println(Thread.currentThread().getName()+"正在执行");});}
运行结果:
使用场景
适用于串行执行任务的场景,一个任务一个任务地执行。
newScheduledThreadPool
public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());}
线程池特点
最大线程数为Integer.MAX_VALUE
阻塞队列是DelayedWorkQueue
keepAliveTime为0
scheduleAtFixedRate() :按某种速率周期执行
scheduleWithFixedDelay():在某个延迟后执行
工作机制
添加一个任务
线程池中的线程从 DelayQueue 中取任务
线程从 DelayQueue 中获取 time 大于等于当前时间的task
执行完后修改这个 task 的 time 为下次被执行的时间
总结
如果你选择了IT行业并坚定的走下去,这个方向肯定是没有一丝问题的,这是个高薪行业,但是高薪是凭自己的努力学习获取来的,这次我把P8大佬用过的一些学习笔记(pdf)都整理在本文中了,如果你有需要的话,请一定点赞分享本文
CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】
《Java中高级核心知识全面解析》
小米商场项目实战,别再担心面试没有实战项目:
tor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());}
线程池特点* 最大线程数为Integer.MAX\_VALUE* 阻塞队列是DelayedWorkQueue* keepAliveTime为0* scheduleAtFixedRate() :按某种速率周期执行* scheduleWithFixedDelay():在某个延迟后执行工作机制* 添加一个任务* 线程池中的线程从 DelayQueue 中取任务* 线程从 DelayQueue 中获取 time 大于等于当前时间的task* 执行完后修改这个 task 的 time 为下次被执行的时间# 总结如果你选择了IT行业并坚定的走下去,这个方向肯定是没有一丝问题的,这是个高薪行业,但是高薪是凭自己的努力学习获取来的,这次我把P8大佬用过的一些学习笔记(pdf)都整理在本文中了,如果你有需要的话,请一定**点赞分享本文****[CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】](https://codechina.csdn.net/m0_60958482/java-p7)****《Java中高级核心知识全面解析》**[外链图片转存中...(img-SH4Nlgaj-1630662432518)]**小米商场项目实战,别再担心面试没有实战项目:**![](https://img-blog.csdnimg.cn/img_convert/ce5f65759882b907654d6d54eecc91da.png)
【BAT面试题系列,网易Java社招面试题相关推荐
- 小米java面试题_小米java社招面试题分享,面经(一面二面)
下面给大家带来的是小米java社招面试题(面经)的相关分享,主要包括了一面二面的面试题,准备小米java一面二面的小伙伴可以来了解一下. 注:面试者是跨行做后台,没有什么项目经验,所以,面试者问的东西 ...
- 小米java社招面试题分享,面经(一面二面)
下面给大家带来的是小米java社招面试题(面经)的相关分享,主要包括了一面二面的面试题,准备小米java一面二面的小伙伴可以来了解一下. 注:面试者是跨行做后台,没有什么项目经验,所以,面试者问的东西 ...
- 网易 java社招面试_《最新面经》—网易Java岗社招面试经历分享
(面试题+答案领取方式:关注公众号回复[666]) 网易面试题 1. 面向对象的特点有哪些? 2. 列举几个java常用的package及其作用 3. 接口和抽象类有什么联系和区别 4. 重载和重写有 ...
- 网易 java社招面试_十月底—最新网易Java岗社招面试经历分享
image (面试题+答案领取方式:关注公众号白楠楠获取) 网易面试题 面向对象的特点有哪些? 列举几个java常用的package及其作用 接口和抽象类有什么联系和区别 重载和重写有什么区别 jav ...
- 2021年Java社招面试题,2021大厂面试合集
前言 前几日有个小老弟找我哭诉,挤破脑袋进Alibaba,三轮技术下来倒在了**缓存技术(Redis+MongDB)**上.听完我这小老弟的一番肺腑之言,这-缓存架构技术:Redis+MongDB,不 ...
- 网易 java社招面试_一个妹子网易Java岗社招面试经历分享
博客群发软件--用 Windows Live Writer完美发布新浪网易blogcnbl 博客群发软件--用 Windows Live Writer完美发布新浪.网易.blogcn.blogbus. ...
- 快手Java社招面试题解析
快手一面 1.要不先做下自我介绍?说一说你最近做的那个项目?然后针对简历提了几个问题 2.mysql 我看你简历里面说做过mysql的优化,说说你都做过哪些优化或者优化的思路? 数据库的索引了解吗?说 ...
- 网易Java社招面试
•写一些类似行测里面的看图选规则之类的题目 •查找出目前正在运行的TCP/UDP服务 •查找出文件test中的所有含"apa"的单词,并保存到文件testc中 •List.Vect ...
- 【面试题系列】Java多线程常见面试题
目录 序言 问题 1.Java中的线程有哪些状态,它们之间是如何转换的? 2.什么是Java中的线程安全?怎么实现 3.Java中线程的创建方法有哪些 3.1 继承Thread类并覆盖run()方法 ...
最新文章
- python格式化文本_Python格式化大文本
- l3fwd 是什么_服务器DPDK l3fwd性能测试
- iOS程序员眼中的客户端免登陆(数据迁移已更新)
- 怀旧服新开服务器最新,暴雪官方发大招,怀旧服新开11个新服务器
- vmware提示com.vmware.sps.fault.QsConnectionException报错
- Java Web(5) Spring 下使用Junit4 单元测试
- CSS+JS灰色树型菜单导航代码
- 异步解耦_如何使用异步生成器解耦业务逻辑
- CSS Grid布局(3)
- Sql Server 学习2
- 怎样将一个Long类型的数据转换成字节数组
- Python 3.9.5 官方中文文档及打包下载
- 卓有成效的管理者(笔记)——如何发挥人的长处
- 觅风易语言智能辅助开发视频教程(高清带源码)
- 谈谈AVG游戏的Android移植(NScripter与吉里吉里)
- 计算机电源 自动关机,电脑自动关机是什么原因,小编教你怎么解决电脑自动关机...
- pcl命名空间:segmentation/extract_clusters.h与segmentation/imp/extract_clusters.hpp
- 基于python的图像灰度值处理(opencv)
- 安卓怎么转移到iphone_如何将联系人从iPhone转移到另一部电话
- Android初学之十二:Broadcast