热门标签 | 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;欢迎沟通交流。




推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 本文介绍了在Windows系统上使用C语言命令行参数启动程序并传递参数的方法,包括接收参数程序的代码和bat文件的编写方法,同时给出了程序运行的结果。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
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社区 版权所有