ExecutorService 接口及相关API细节详解。

Java Magazine上面有一个专门坑人的面试题系列: https://blogs.oracle.com/javamagazine/quiz-2。

这些问题的设计宗旨,主要是测试面试者对Java语言的了解程度,而不是为了用弯弯绕绕的手段把面试者搞蒙。

如果你看过往期的问题,就会发现每一个都不简单。

这些试题模拟了认证考试中的一些难题。 而 “中级(intermediate)” 和 “高级(advanced)” 指的是试题难度,而不是说这些知识本身很深。 一般来说,“高级”问题会稍微难一点。

问题(高级难度)

此问题的目的是考察如何通过 RunnableCallable 来创建任务,并使用 ExecutorService 来并发执行。

我们有一个 Logger 类,定义如下所示:

class Logger implements Runnable {String msg;public Logger(String msg) {this.msg = msg;}public void run() {System.out.print(msg);}
}

并给出如下使用的代码:

Stream<Logger> s = Stream.of(new Logger("Error "),new Logger("Warning "),new Logger("Debug "));
ExecutorService es =Executors.newCachedThreadPool();
s.sequential().forEach(l -> es.execute(l));
es.shutdown();
es.awaitTermination(10, TimeUnit.SECONDS);

这里省略了相关的 import 语句, 假设代码能编译并正常启动。 请选择两项可能的输出结果:

  • A、 Error Debug Warning
  • B、 Error Warning Debug
  • C、 Error Error Debug
  • D、 Error Debug

答案和解析

这道试题属于 Executors 类和 ExecutorService 接口相关的考点,顺带考察 Executors 工具类自带的 ExecutorService 线程池实现。

在Java的早期版本中,需要程序员手工创建和管理线程。线程是系统内核级的重要资源,并不能无限创建; 而且创建线程的开销很大,所以开发中一般会使用资源池模式,也就是创建 “线程池”。通过线程池,可以用少量的线程,来执行大量的任务。
线程池的思路是这样的: 与其为每个任务创建一个线程,执行完就销毁; 倒不如统一创建少量的线程, 然后将任务逻辑用 Runnable 包装起来, 提交给线程池来调度执行。
有任务需要调度的时候,线程池找一个空闲的线程,并通知他干活。 任务执行完成后,再将这个线程放回池子里,等待下一次调度。

Java 5.0 开始提供标准的线程池API。 通过 ExecutorExecutorService 接口定义了线程池以及支持的交互操作。
另外,我们可以使用 Executors 的静态工厂方法来实例化 ExecutorService 的各种实现。
相关的基础类和接口都位于 java.util.concurrent 包中, 在编写简单的并发任务时,可以直接使用。

Executor 是顶层接口, 定义了执行 Runnable 任务的方法;但我们一般用的是子接口 ExecutorService 及其实现。
ExecutorService 接口中增加了处理 Callable 的方法, 以及关闭线程池的功能。
实现 Callable 接口的任务会返回一个结果, 调用方可以通过提交任务时返回的 Future 对象,来异步获取任务的执行状态和结果,这样就对任务有了一定的管理和控制能力。

ExecutorExecutorService 接口并没有规定使用哪种调度策略来执行。

  • 有些线程池,使用固定数量的线程来并发地执行任务,新提交的任务要等到有空闲线程才会被执行。
  • 有的线程池, 在工作负载上升时自动增加线程,并在需求降低时清理掉一部分线程。
  • 还有的线程池只使用单个线程,直接按顺序执行提交的任务。

这些特征取决于具体的实现,需要开发者根据业务系统的特征来权衡,并选择适当的线程池。 针对这几类线程池,Executors 工具类提供了三种工厂方法:

  • newFixedThreadPool
  • newCachedThreadPool
  • newSingleThreadExecutor

前两个方法创建的线程池可以有多个worker线程, 而 newSingleThreadExecutor 方法创建的线程池则只有单个线程。

回到前面的问题, 试题中给出的代码创建了缓存模式的线程池。
这类线程池会根据需要生成新的worker线程,并清理一段时间内没有使用到的线程。
但缓存模式的线程池有一个严重缺点: 创建的线程数有可能不被限制, 那样的话会导致大量的资源占用。 在高负载场景下,可能会由于资源争用而导致性能急剧下降。

因为创建的线程池具有多个线程, 所以后面提交的任务可以并发执行。
无论谁先开始,我们都无法对其执行进度做出精确预测。
也就是说,他们输出消息的顺序可能是任意的。
由此得知, 选项A选项B正确

ExecutorService 会保证提交的任务最多被执行一次。
在某些情况下,任务可能不会执行,或者在执行完成之前线程池就被关闭了。
因为具有最多执行一次的特征,所以我们不会看到任何重复的消息。因此可以判断,选项C不正确

在调用 shutdown 方法之后,ExecutorService 会拒绝新的任务提交请求, 但已有的任务会继续运行,直到所有的作业全部执行完才会关闭。
因此在这里给的代码中, 三个消息都会看到。 因此可知,选项D不正确

顺便提一句,可能有些读者会认为,如果在10秒内执行不完, 那么选项D也可能是正确的。
但反过来说,如何确定这个消息会被打印呢?

因为试题中给出的任务逻辑非常简单,很明显不可能10秒钟还执行不完。
而且我们通过分析能判断出 选项A选项B 是正确的, 那么做题时就可以将这种不可能的情况排除。

当然,你可能对选项D感兴趣,因为在其他某些极端的情况下, 作业无法在10秒内完成,比如恰好在这个时刻操作系统启动升级或更新。
请注意,在给定的代码中,没有任何证据表明 JVM 将被强行关闭。
而且默认创建的线程都是 非守护线程(nondaemon thread),因此,在作业完成之前,JVM 不会退出。

所以,如果允许程序运行,则对应的消息都会被打印出来。

总结

正确的选项是 AB

相关链接

  • Java坑人面试题系列: 包装类(中级难度)
  • Java坑人面试题系列: 比对while与for循环(中级)
  • Java坑人面试题系列: 集合(高级)
  • Java坑人面试题系列: 线程/线程池(高级)
  • Java坑人面试题系列: 变量声明(中级)

原文链接: https://blogs.oracle.com/javamagazine/quiz-advanced-executor-service

Java坑人面试题系列: 线程/线程池(高级难度)相关推荐

  1. Java坑人面试题系列: 比对while与for循环(中级难度)

    Java Magazine上面有一个专门坑人的面试题系列: https://blogs.oracle.com/javamagazine/quiz-2. 这些问题的设计宗旨,主要是测试面试者对Java语 ...

  2. 【Java实习生面试题系列】-- 多线程篇四

    文章目录 1. 说下对同步器 AQS 的理解? 2. AQS 的原理是什么? 3. AQS 底层使用了模板方法模式,你能说出几个需要重写的方法吗? 4. 说下对 Semaphore.CountDown ...

  3. 【Java实习生面试题系列】-- JVM篇一

    文章目录 1. 说一下 JVM 的主要组成部分?及其作用? 2. 堆和栈的区别是什么? 3. 对象的访问定位的两种方式? 4. 判断垃圾可以回收的方法有哪些? 5. 被标记为垃圾的对象一定会被回收吗? ...

  4. Java经典面试题:一个线程两次调用start()方法会出现什么情况?

    大家好,我是 Oracle首席工程师杨晓峰. 今天想和大家深入聊聊线程,相信大家对于线程这个概念都不陌生,它是Java并发的基础元素,理解.操纵.诊断线程是Java工程师的必修课,但是你真的掌握线程了 ...

  5. java 批量插入clob_SpringBoot系列(16)线程池Executors并发编程之批量查询-插入数据

    在上篇文章中Debug给大家分享介绍了"Java线程池-多线程的其中一种应用场景~广播式给所有有效用户发送邮件(通知)",本篇文章我们将继续向前迈进,继续介绍并实战"线程 ...

  6. Java多线程面试题:子线程循环10次,接着主线程循环100,接着又回到子线程循环10次, 接着再回到主线程又循环100,如此循环50次

    近在学习多线程,特贴在这里,并附上自己所作答案(参考与张孝祥老师的方法). 题目如下: 子线程循环10次,接着主线程循环100,接着又回到子线程循环10次, 接着再回到主线程又循环100,如此循环50 ...

  7. Java基础面试题系列

    本文收集了一些经典的Java面试题 1.面向对象的特征有哪些方面? 答:面向对象的特征主要有以下几个方面: 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面.抽象只关 ...

  8. 【Java实习生面试题系列】-- 多线程篇一

    文章目录 1. 并行和并发有什么区别? 2. 线程和进程的区别? 3. 守护线程是什么? 4. 创建线程的几种方式? 5. Runnable 和 Callable 有什么区别? 6. 线程状态及转换? ...

  9. 一文整理总结常见Java后端面试题系列——Java虚拟机篇(2022最新版)

    关于作者

最新文章

  1. Ubuntu 系统如何修改主机名
  2. 使用特异性特征提取网络辅助分类任务
  3. java mvc httpget怎么使用_springMVC正确使用GET POST PUT和DELETE方法,如何传递参数
  4. java redis 批量删除key_Redis,就是这么朴实无华
  5. 查询优化器内核剖析第一篇
  6. css float(脱离正常流:向左/右浮动直到父元素/另一float, 不占空间)
  7. tomcat 启动异常 The web application [ROOT] registered the JDBC driver [xxx] but failed to unregister it
  8. Nanachi发布:基于 React 的多端小程序转译框架
  9. git 初始化git存储库_什么不保存到Git存储库中
  10. 先虚拟主机后云服务器,先虚拟主机后云服务器
  11. 详解RecyclerView下拉刷新与上拉更多
  12. 定时器精度对性能的影响_Comet CAA-500天线分析仪 | 高精度模拟十字针同时显示SWR和阻抗...
  13. sql ddl中key_SQL DDL:SQL Server中SQL DDL命令入门
  14. CSS——简写属性(在padding和margin这样的简写属性中,值赋值的顺序是top、right、bottom、left)...
  15. 中国游戏的未来在哪里 - 游戏行业20年历史观察及趋势分析
  16. 【模板一】计算机XXXX系统-毕业设计
  17. WebSockt面试题
  18. 用python计算直角三角形斜边长
  19. 迅睿cms模板,迅睿cms模板建站,迅睿模板主题开发
  20. 树莓派第一次使用WIN10电脑远程连接(无显示器)

热门文章

  1. 联想服务器万全T260G3系统,联想万全T260G3服务器企业高性价比之选
  2. EI主题词新版界面查询-Engineering Village
  3. python管理系统web版_基于Python Flask的web日程管理系统
  4. 用ubuntu玩dota2
  5. 重排正数和负数(将所有正数排在负数前面)
  6. 怎样规划你毕业以后的人生 收藏
  7. win7怎么不锁定计算机,问题1: 计算机屏幕如何不能自动锁定win7系统?
  8. 基于JAVA的学生考勤管理系统可行性分析
  9. 下四国的排长和下四国的小兵 (转载)
  10. 四步搞定64位win7安装CAD2008