热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

池流程图_面试刷题36:线程池的原理和使用方法?

线程池原理和使用在面试中被高频问到,比如阿里的面试题。下面我们针对问题来进行回答。为什么要使用线程池?线程池的使用场景有2:1࿰
81890ed212a6789821c1e66dd8bd9cf8.png

线程池原理和使用在面试中被高频问到,比如阿里的面试题。下面我们针对问题来进行回答。

为什么要使用线程池?

线程池的使用场景有2:

1, 高并发场景:比如tomcat的处理机制,内置了线程池处理http请求;

2,异步任务处理:比如spring的异步方法改造,增加@Asyn注解对应了一个线程池;

使用线程池带来的好处有4:

1, 降低系统的消耗:线程池复用了内部的线程对比处理任务的时候创建线程处理完毕销毁线程降低了线程资源消耗

2,提高系统的响应速度:任务不必等待新线程创建,直接复用线程池的线程执行

3,提高系统的稳定性:线程是重要的系统资源,无限制创建系统会奔溃,线程池复用了线程,系统会更稳定

4,提供了线程的可管理功能:暴露了方法,可以对线程进行调配,优化和监控

线程池的实现原理

线程池处理任务流程

当向线程池中提交一个任务,线程池内部是如何处理任务的?

先来个流程图,标识一下核心处理步骤:

fdea640675febefc1f759e502dfeb792.png

1,线程池内部会获取activeCount, 判断活跃线程的数量是否大于等于corePoolSize(核心线程数量),如果没有,会使用全局锁锁定线程池,创建工作线程,处理任务,然后释放全局锁;

2,判断线程池内部的阻塞队列是否已经满了,如果没有,直接把任务放入阻塞队列;

3,判断线程池的活跃线程数量是否大于等于maxPoolSize,如果没有,会使用全局锁锁定线程池,创建工作线程,处理任务,然后释放全局锁;

4,如果以上条件都满足,采用饱和处理策略处理任务。

说明:使用全局锁是一个严重的可升缩瓶颈,在线程池预热之后(即内部线程数量大于等于corePoolSize),任务的处理是直接放入阻塞队列,这一步是不需要获得全局锁的,效率比较高。

源码如下:

public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); if (workerCountOf(c)

注释没保留,注释的内容就是上面画的流程图; 代码的逻辑就是流程图中的逻辑。

线程池中的线程执行任务

执行任务模型如下:

d270a219d861e94cdbe4a06dc6338ef2.png

线程池中的线程执行任务分为以下两种情况:

1, 创建一个线程,会在这个线程中执行当前任务;

2,工作线程完成当前任务之后,会死循环从BlockingQueue中获取任务来执行;

代码如下:

private boolean addWorker(Runnable firstTask, boolean core) { retry: for (int c = ctl.get();;) { // Check if queue empty only if necessary. if (runStateAtLeast(c, SHUTDOWN) && (runStateAtLeast(c, STOP) || firstTask != null || workQueue.isEmpty())) return false; for (;;) { if (workerCountOf(c) >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK)) return false; if (compareAndIncrementWorkerCount(c)) break retry; c = ctl.get(); // Re-read ctl if (runStateAtLeast(c, SHUTDOWN)) continue retry; // else CAS failed due to workerCount change; retry inner loop } } boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { // Recheck while holding lock. // Back out on ThreadFactory failure or if // shut down before lock acquired. int c = ctl.get(); if (isRunning(c) || (runStateLessThan(c, STOP) && firstTask == null)) { if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); workers.add(w); int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { //释放锁 mainLock.unlock(); } if (workerAdded) { //执行提交的任务,然后设置工作线程为启动状态 t.start(); workerStarted = true; } } } finally { if (! workerStarted) addWorkerFailed(w); } return workerStarted; }

从代码中可以看到:把工作线程增加到线程池,然后释放锁,执行完提交进来的任务之后,新建的工作线程状态为启动状态;

线程池的使用

创建线程池

创建线程池使用线程池的构造函数来创建。

/** * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters. * * @param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param maximumPoolSize the maximum number of threads to allow in the * pool * @param keepAliveTime when the number of threads is greater than * the core, this is the maximum time that excess idle threads * will wait for new tasks before terminating. * @param unit the time unit for the {@code keepAliveTime} argument * @param workQueue the queue to use for holding tasks before they are * executed. This queue will hold only the {@code Runnable} * tasks submitted by the {@code execute} method. * @param threadFactory the factory to use when the executor * creates a new thread * @param handler the handler to use when execution is blocked * because the thread bounds and queue capacities are reached * @throws IllegalArgumentException if one of the following holds:
* {&#64;code corePoolSize <0}
* {&#64;code keepAliveTime <0}
* {&#64;code maximumPoolSize <&#61; 0}
* {&#64;code maximumPoolSize

参数简单翻译过来&#xff0c;然后做一下备注&#xff1a;

199779b16f783e1405917fcb38de76d2.png

RejectedExecutionHandler分为4种&#xff1a;

Abort:直接抛出异常

Discard:静默丢弃最后的任务

DiscardOldest:静默丢弃最先入队的任务&#xff0c;并处理当前任务

CallerRuns:调用者线程来执行任务

也可以自定义饱和策略。实现RejectedExecutionHandler即可。

线程池中提交任务

线程池中提交任务的方法有2&#xff1a; 1&#xff0c;void execute(Runable) ,没有返回值&#xff0c;无法判断任务的执行状态。

2&#xff0c;Future submit(Callable) ,有返回值&#xff0c;可以根据返回的Future对象来判断任务的执行状态&#xff0c;也可以调用get方法来同步阻塞当前线程获取结果&#xff0c;或者采用get方法的超时版本&#xff0c;防止阻塞超时的发生。

代码如下&#xff1a;

public interface Executor { /** * Executes the given command at some time in the future. The command * may execute in a new thread, in a pooled thread, or in the calling * thread, at the discretion of the {&#64;code Executor} implementation. * * &#64;param command the runnable task * &#64;throws RejectedExecutionException if this task cannot be * accepted for execution * &#64;throws NullPointerException if command is null */ void execute(Runnable command);}

Future submit(Callable task);

关闭线程池

关闭线程池方法有2&#xff1a;

1,shutdown();

2,shutdownNow();

两种关闭的方法区别如下表&#xff1a;

f8b021adbf59a69991d5dcdda4253220.png

关闭原理都是调用线程的interrupt()方法来中断所有的工作线程&#xff0c;所以无法中断的线程的任务可能永远没法终止。

只要调用了以上两个方法&#xff0c;isShutdown&#61;true;只有所有的工作线程都关闭&#xff0c;isTerminaed&#61;true;

如何合理的配置线程池参数&#xff1f;

分如下场景&#xff0c;参考选择依据如下&#xff1a;

2e230ac624af3f7ade58448851e1e3c7.png

队列的使用推荐使用有界队列&#xff0c;提高系统的稳定性和预警能力。

监控线程池

场景&#xff1a;当线程池出现问题&#xff0c;可以根据监控数据快速定位和解决问题。

线程池提供的主要监控参数&#xff1a;

027b9708debd9dfdb57e3b2b4a0f604c.png

也可以自定义监控,通过自定义线程池&#xff0c;实现beforeExecute,afterExecute,terminated方法&#xff0c;可以在任务执行前&#xff0c;任务执行后&#xff0c;线程池关闭前记录监控数据。

小结

本篇先从使用场景和优点出发分析了为什么要使用线程池。

然后介绍了线程池中任务的执行过程&#xff0c;以及工作线程处理任务的两种方式。

最后介绍了如何使用线程池&#xff0c;创建&#xff0c;销毁&#xff0c;提交任务&#xff0c;监控&#xff0c;设置合理的参数调优等方面。

我会持续分享Java软件编程知识和程序员发展职业之路&#xff0c;欢迎关注&#xff01; 原创不易&#xff0c;点赞关注支持一下吧&#xff01;转载请注明出处&#xff0c;让我们互通有无&#xff0c;共同进步&#xff0c;欢迎沟通交流。




推荐阅读
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • 掌握远程执行Linux脚本和命令的技巧
    本文将详细介绍如何利用Python的Paramiko库实现远程执行Linux脚本和命令,帮助读者快速掌握这一实用技能。通过具体的示例和详尽的解释,让初学者也能轻松上手。 ... [详细]
  • 本文探讨了 Spring Boot 应用程序在不同配置下支持的最大并发连接数,重点分析了内置服务器(如 Tomcat、Jetty 和 Undertow)的默认设置及其对性能的影响。 ... [详细]
  • 并发编程:深入理解设计原理与优化
    本文探讨了并发编程中的关键设计原则,特别是Java内存模型(JMM)的happens-before规则及其对多线程编程的影响。文章详细介绍了DCL双重检查锁定模式的问题及解决方案,并总结了不同处理器和内存模型之间的关系,旨在为程序员提供更深入的理解和最佳实践。 ... [详细]
  • 实体映射最强工具类:MapStruct真香 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 深入解析 Spring Security 用户认证机制
    本文将详细介绍 Spring Security 中用户登录认证的核心流程,重点分析 AbstractAuthenticationProcessingFilter 和 AuthenticationManager 的工作原理。通过理解这些组件的实现,读者可以更好地掌握 Spring Security 的认证机制。 ... [详细]
  • 微软Exchange服务器遭遇2022年版“千年虫”漏洞
    微软Exchange服务器在新年伊始遭遇了一个类似于‘千年虫’的日期处理漏洞,导致邮件传输受阻。该问题主要影响配置了FIP-FS恶意软件引擎的Exchange 2016和2019版本。 ... [详细]
  • FinOps 与 Serverless 的结合:破解云成本难题
    本文探讨了如何通过 FinOps 实践优化 Serverless 应用的成本管理,提出了首个 Serverless 函数总成本估计模型,并分享了多种有效的成本优化策略。 ... [详细]
  • 本文详细介绍了 org.apache.commons.io.IOCase 类中的 checkCompareTo() 方法,通过多个代码示例展示其在不同场景下的使用方法。 ... [详细]
  • 本文探讨了使用C#在SQL Server和Access数据库中批量插入多条数据的性能差异。通过具体代码示例,详细分析了两种数据库的执行效率,并提供了优化建议。 ... [详细]
  • 本文介绍了如何在多线程环境中实现异步任务的事务控制,确保任务执行的一致性和可靠性。通过使用计数器和异常标记字段,系统能够准确判断所有异步线程的执行结果,并根据结果决定是否回滚或提交事务。 ... [详细]
  • 本文介绍如何在Spring Boot项目中集成Redis,并通过具体案例展示其配置和使用方法。包括添加依赖、配置连接信息、自定义序列化方式以及实现仓储接口。 ... [详细]
  • 本文详细介绍了优化DB2数据库性能的多种方法,涵盖统计信息更新、缓冲池调整、日志缓冲区配置、应用程序堆大小设置、排序堆参数调整、代理程序管理、锁机制优化、活动应用程序限制、页清除程序配置、I/O服务器数量设定以及编入组提交数调整等方面。通过这些技术手段,可以显著提升数据库的运行效率和响应速度。 ... [详细]
  • Spring Boot单元测试中Redis连接失败的解决方案
    本文探讨了在Spring Boot项目中进行单元测试时遇到Redis连接问题的原因及解决方法,详细分析了配置文件加载路径不当导致的问题,并提供了有效的解决方案。 ... [详细]
author-avatar
kingkongkoil
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有