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




推荐阅读
  • JUC并发编程——线程的基本方法使用
    目录一、线程名称设置和获取二、线程的sleep()三、线程的interrupt四、join()五、yield()六、wait(),notify(),notifyAll( ... [详细]
  • Java虚拟机及其发展历程
    Java虚拟机(JVM)是每个Java开发者日常工作中不可或缺的一部分,但其背后的运作机制却往往显得神秘莫测。本文将探讨Java及其虚拟机的发展历程,帮助读者深入了解这一关键技术。 ... [详细]
  • 我的读书清单(持续更新)201705311.《一千零一夜》2006(四五年级)2.《中华上下五千年》2008(初一)3.《鲁滨孙漂流记》2008(初二)4.《钢铁是怎样炼成的》20 ... [详细]
  • spring boot使用jetty无法启动 ... [详细]
  • 本文详细介绍了如何正确设置Shadowsocks公共代理,包括调整超时设置、检查系统限制、防止滥用及遵守DMCA法规等关键步骤。 ... [详细]
  • 从CodeIgniter中提取图像处理组件
    本指南旨在帮助开发者在未使用CodeIgniter框架的情况下,如何独立使用其强大的图像处理功能,包括图像尺寸调整、创建缩略图、裁剪、旋转及添加水印等。 ... [详细]
  • RTThread线程间通信
    线程中通信在裸机编程中,经常会使用全局变量进行功能间的通信,如某些功能可能由于一些操作而改变全局变量的值,另一个功能对此全局变量进行读取& ... [详细]
  • pypy 真的能让 Python 比 C 还快么?
    作者:肖恩顿来源:游戏不存在最近“pypy为什么能让python比c还快”刷屏了,原文讲的内容偏理论,干货比较少。我们可以再深入一点点,了解pypy的真相。正式开始之前,多唠叨两句 ... [详细]
  • 本文详细探讨了Spring框架中遇到的NoSuchBeanDefinitionException异常,具体涉及com.thinkplatform.dao.UserLogDao Bean未定义的问题,并提供了相应的解决方案。 ... [详细]
  • 本文介绍了 PHP 的基本概念、服务器与客户端的工作原理,以及 PHP 如何与数据库交互。同时,还涵盖了常见的数据库操作和安全性问题。 ... [详细]
  • 本文介绍了如何将Spring属性占位符与Jersey的@Path和@ApplicationPath注解结合使用,以便在资源路径中动态解析属性值。 ... [详细]
  • 【MySQL】frm文件解析
    官网说明:http:dev.mysql.comdocinternalsenfrm-file-format.htmlfrm是MySQL表结构定义文件,通常frm文件是不会损坏的,但是如果 ... [详细]
  • Spring Security基础配置详解
    本文详细介绍了Spring Security的基础配置方法,包括如何搭建Maven多模块工程以及具体的安全配置步骤,帮助开发者更好地理解和应用这一强大的安全框架。 ... [详细]
  • Asynchronous JavaScript and XML (AJAX) 的流行很大程度上得益于 Google 在其产品如 Google Suggest 和 Google Maps 中的应用。本文将深入探讨 AJAX 在 .NET 环境下的工作原理及其实现方法。 ... [详细]
  • HTML前端开发:UINavigationController与页面间数据传递详解
    本文详细介绍了如何在HTML前端开发中利用UINavigationController进行页面管理和数据传递,适合初学者和有一定基础的开发者学习。 ... [详细]
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社区 版权所有