经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
关于《Java并发编程之线程池十八问》的补充内容
来源:cnblogs  作者:JavaBuild  时间:2024/5/31 9:13:45  对本文有异议

一、写在开头

在上一篇文章我们写《Java并发编程之线程池十八问》的时候,鉴于当时的篇幅已经过长,很多内容就没有扩展了,在这篇文章里对一些关键知识点进行对比补充。

二、Runnable vs Callable

在创建线程的时候,一般会选用 RunnableCallable 两种方式。

【源码对比】

Runnable接口

  1. @FunctionalInterface
  2. public interface Runnable {
  3. /**
  4. * 被线程执行,没有返回值也无法抛出异常
  5. */
  6. public abstract void run();
  7. }

Callable接口

  1. @FunctionalInterface
  2. public interface Callable<V> {
  3. /**
  4. * 计算结果,或在无法这样做时抛出异常。
  5. * @return 计算得出的结果
  6. * @throws 如果无法计算结果,则抛出异常
  7. */
  8. V call() throws Exception;
  9. }
  1. Runnable自 Java 1.0 以来一直存在,Callable在 Java 1.5 时引入;
  2. Runnable 接口不会返回结果或抛出检查异常,Callable 接口可以;
  3. Callable支持泛型,可定义返回值类型,但一般情况下没有返回值时,我们推荐使用Runnable接口,使得代码更简洁!
  4. 工具类 Executors 可以实现将 Runnable 对象转换成 Callable 对象。(Executors.callable(Runnable task) 或 Executors.callable(Runnable task, Object result))。

三、execute() vs submit()

在线程池中我们有两种提交任务的方式,分别是 execute()submit(),虽然我们在上一篇文章中都有用到,但是并没对它们的特点进行总结,这里做一个对比:

  1. execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
  2. submit()方法用于提交需要返回值的任务。线程池会返回一个 Future 类型的对象,通过这个 Future 对象可以判断任务是否执行成功,并且可以通过 Future 的 get()方法来获取返回值。
  1. //这里使用Executors只是方便测试,正常使用时推荐使用ThreadPoolExecutor!
  2. ExecutorService executorService = Executors.newFixedThreadPool(3);
  3. Future<String> submit = executorService.submit(() -> {
  4. try {
  5. Thread.sleep(5000L);
  6. } catch (InterruptedException e) {
  7. e.printStackTrace();
  8. }
  9. return "javabuild";
  10. });
  11. String s = submit.get();
  12. System.out.println(s);
  13. executorService.shutdown();

输出:

  1. javabuild

如果一直没有获取到返回结果,会报错,使用get(long timeout,TimeUnit unit)方法的话,如果在 timeout 时间内任务还没有执行完,就会抛出 java.util.concurrent.TimeoutException。

四、shutdown() vs shutdownNow()

在JDK 1.8 中,线程池的停止一般使用 shutdown()、shutdownNow()这两种方法。

方法一: shutdown()

  1. public void shutdown() {
  2. final ReentrantLock mainLock = this.mainLock; // ThreadPoolExecutor的主锁
  3. mainLock.lock(); // 加锁以确保独占访问
  4. try {
  5. checkShutdownAccess(); // 检查是否有关闭的权限
  6. advanceRunState(SHUTDOWN); // 将执行器的状态更新为SHUTDOWN
  7. interruptIdleWorkers(); // 中断所有闲置的工作线程
  8. onShutdown(); // ScheduledThreadPoolExecutor中的挂钩方法,可供子类重写以进行额外操作
  9. } finally {
  10. mainLock.unlock(); // 无论try块如何退出都要释放锁
  11. }
  12. tryTerminate(); // 如果条件允许,尝试终止执行器
  13. }

在shutdown的源码中,会启动一次顺序关闭,在这次关闭中,执行器不再接受新任务,但会继续处理队列中的已存在任务,当所有任务都完成后,线程池中的线程会逐渐退出。

方法二: shutdown()

  1. /**
  2. * 尝试停止所有正在执行的任务,停止处理等待的任务,
  3. * 并返回等待处理的任务列表。
  4. *
  5. * @return 从未开始执行的任务列表
  6. */
  7. public List<Runnable> shutdownNow() {
  8. List<Runnable> tasks; // 用于存储未执行的任务的列表
  9. final ReentrantLock mainLock = this.mainLock; // ThreadPoolExecutor的主锁
  10. mainLock.lock(); // 加锁以确保独占访问
  11. try {
  12. checkShutdownAccess(); // 检查是否有关闭的权限
  13. advanceRunState(STOP); // 将执行器的状态更新为STOP
  14. interruptWorkers(); // 中断所有工作线程
  15. tasks = drainQueue(); // 清空队列并将结果放入任务列表中
  16. } finally {
  17. mainLock.unlock(); // 无论try块如何退出都要释放锁
  18. }
  19. tryTerminate(); // 如果条件允许,尝试终止执行器
  20. return tasks; // 返回队列中未被执行的任务列表
  21. }

与shutdown不同的是shutdownNow会尝试终止所有的正在执行的任务,清空队列,停止失败会抛出异常,并且返回未被执行的任务列表。

五、isTerminated() vs isShutdown()

  1. isShutDown 当调用 shutdown() 或shutdownNow()方法后返回为 true;
  2. isTerminated 当调用 shutdown() 方法后,并且所有提交的任务完成后返回为 true;当调用shutdownNow()方法后,成功停止后返回true;
  3. 当线程池任务都正常完成的话,则这两种方法均为false。

六、结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

原文链接:https://www.cnblogs.com/JavaBuild/p/18223767

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号