Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java高并发之线程池详解

Java高并发之线程池详解

作者头像
用户1216491
发布于 2018-08-01 02:06:04
发布于 2018-08-01 02:06:04
66900
代码可运行
举报
文章被收录于专栏:xdecodexdecode
运行总次数:0
代码可运行

线程池优势

在业务场景中, 如果一个对象创建销毁开销比较大, 那么此时建议池化对象进行管理.

例如线程, jdbc连接等等, 在高并发场景中, 如果可以复用之前销毁的对象, 那么系统效率将大大提升.

另外一个好处是可以设定池化对象的上限, 例如预防创建线程数量过多导致系统崩溃的场景.

jdk中的线程池

下文主要从以下几个角度讲解:

  • 创建线程池
  • 提交任务
  • 潜在宕机风险
  • 线程池大小配置
  • 自定义阻塞队列BlockingQueue
  • 回调接口
  • 自定义拒绝策略
  • 自定义ThreadFactory
  • 关闭线程池

创建线程池

我们可以通过自定义ThreadPoolExecutor或者jdk内置的Executors来创建一系列的线程池

  • newFixedThreadPool: 创建固定线程数量的线程池
  • newSingleThreadExecutor: 创建单一线程的池
  • newCachedThreadPool: 创建线程数量自动扩容, 自动销毁的线程池
  • newScheduledThreadPool: 创建支持计划任务的线程池

 上述几种都是通过new ThreadPoolExecutor()来实现的, 构造函数源码如下: 

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1     /**
 2      * @param corePoolSize 池内核心线程数量, 超出数量的线程会进入阻塞队列
 3      * @param maximumPoolSize 最大可创建线程数量
 4      * @param keepAliveTime 线程存活时间
 5      * @param unit 存活时间的单位
 6      * @param workQueue 线程溢出后的阻塞队列
 7      */
 8     public ThreadPoolExecutor(int corePoolSize,
 9                               int maximumPoolSize,
10                               long keepAliveTime,
11                               TimeUnit unit,
12                               BlockingQueue<Runnable> workQueue) {
13         this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
14     }
15 
16     public static ExecutorService newFixedThreadPool(int nThreads) {
17         return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
18     }
19 
20     public static ExecutorService newSingleThreadExecutor() {
21         return new Executors.FinalizableDelegatedExecutorService
22                 (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
23     }
24 
25     public static ExecutorService newCachedThreadPool() {
26         return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
27     }
28 
29     public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
30         return new ScheduledThreadPoolExecutor(corePoolSize);
31     }
32 
33     public ScheduledThreadPoolExecutor(int corePoolSize) {
34         super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue());
35     }

提交任务

直接调用executorService.execute(runnable)或者submit(runnable)即可,

execute和submit的区别在于submit会返回Future来获取任何执行的结果.

我们看下newScheduledThreadPool的使用示例.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1 public class SchedulePoolDemo {
 2 
 3     public static void main(String[] args){
 4         ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
 5         // 如果前面的任务没有完成, 调度也不会启动
 6         service.scheduleAtFixedRate(new Runnable() {
 7             @Override
 8             public void run() {
 9                 try {
10                     Thread.sleep(2000);
11                     // 每两秒打印一次.
12                     System.out.println(System.currentTimeMillis()/1000);
13                 } catch (InterruptedException e) {
14                     e.printStackTrace();
15                 }
16             }
17         }, 0, 2, TimeUnit.SECONDS);
18     }
19 }

潜在宕机风险

使用Executors来创建要注意潜在宕机风险.其返回的线程池对象的弊端如下:

  • FixedThreadPool和SingleThreadPoolPool : 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM.
  • CachedThreadPool和ScheduledThreadPool : 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM.

综上所述, 在可能有大量请求的线程池场景中, 更推荐自定义ThreadPoolExecutor来创建线程池, 具体构造函数配置见下文.

线程池大小配置

一般根据任务类型进行区分, 假设CPU为N核

  • CPU密集型任务需要减少线程数量, 降低线程之间切换造成的开销, 可配置线程池大小为N + 1.
  • IO密集型任务则可以加大线程数量, 可配置线程池大小为 N * 2.
  • 混合型任务则可以拆分为CPU密集型与IO密集型, 独立配置.

自定义阻塞队列BlockingQueue

主要存放等待执行的线程, ThreadPoolExecutor中支持自定义该队列来实现不同的排队队列.

  • ArrayBlockingQueue:先进先出队列,创建时指定大小, 有界;
  • LinkedBlockingQueue:使用链表实现的先进先出队列,默认大小为Integer.MAX_VALUE;
  • SynchronousQueue:不保存提交的任务, 数据也不会缓存到队列中, 用于生产者和消费者互等对方, 一起离开.
  • PriorityBlockingQueue: 支持优先级的队列

回调接口

线程池提供了一些回调方法, 具体使用如下所示.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1         ExecutorService service = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>()) {
 2 
 3             @Override
 4             protected void beforeExecute(Thread t, Runnable r) {
 5                 System.out.println("准备执行任务: " + r.toString());
 6             }
 7 
 8             @Override
 9             protected void afterExecute(Runnable r, Throwable t) {
10                 System.out.println("结束任务: " + r.toString());
11             }
12 
13             @Override
14             protected void terminated() {
15                 System.out.println("线程池退出");
16             }
17         };

可以在回调接口中, 对线程池的状态进行监控, 例如任务执行的最长时间, 平均时间, 最短时间等等, 还有一些其他的属性如下:

  • taskCount:线程池需要执行的任务数量.
  • completedTaskCount:线程池在运行过程中已完成的任务数量.小于或等于taskCount.
  • largestPoolSize:线程池曾经创建过的最大线程数量.通过这个数据可以知道线程池是否满过.如等于线程池的最大大小,则表示线程池曾经满了.
  • getPoolSize:线程池的线程数量.如果线程池不销毁的话,池里的线程不会自动销毁,所以这个大小只增不减.
  • getActiveCount:获取活动的线程数.

自定义拒绝策略

线程池满负荷运转后, 因为时间空间的问题, 可能需要拒绝掉部分任务的执行.

jdk提供了RejectedExecutionHandler接口, 并内置了几种线程拒绝策略

  • AbortPolicy: 直接拒绝策略, 抛出异常.
  • CallerRunsPolicy: 调用者自己执行任务策略.
  • DiscardOldestPolicy: 舍弃最老的未执行任务策略.

使用方式也很简单, 直接传参给ThreadPool

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1         ExecutorService service = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, 
2                 new SynchronousQueue<Runnable>(),
3                 Executors.defaultThreadFactory(),
4                 new RejectedExecutionHandler() {
5                     @Override
6                     public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
7                         System.out.println("reject task: " + r.toString());
8                     }
9                 });

自定义ThreadFactory

线程工厂用于创建池里的线程. 例如在工厂中都给线程setDaemon(true), 这样程序退出的时候, 线程自动退出.

或者统一指定线程优先级, 设置名称等等.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 1 class NamedThreadFactory implements ThreadFactory {
 2     private static final AtomicInteger threadIndex = new AtomicInteger(0);
 3     private final String baseName;
 4     private final boolean daemon;
 5 
 6     public NamedThreadFactory(String baseName) {
 7         this(baseName, true);
 8     }
 9 
10     public NamedThreadFactory(String baseName, boolean daemon) {
11         this.baseName = baseName;
12         this.daemon = daemon;
13     }
14 
15     public Thread newThread(Runnable runnable) {
16         Thread thread = new Thread(runnable, this.baseName + "-" + threadIndex.getAndIncrement());
17         thread.setDaemon(this.daemon);
18         return thread;
19     }
20 }

关闭线程池

跟直接new Thread不一样, 局部变量的线程池, 需要手动关闭, 不然会导致线程泄漏问题.

默认提供两种方式关闭线程池.

  • shutdown: 等所有任务, 包括阻塞队列中的执行完, 才会终止, 但是不会接受新任务.
  • shutdownNow: 立即终止线程池, 打断正在执行的任务, 清空队列. 
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-06-04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Spring Boot学习笔记(二)Windows下IDEA 配置Maven
maven指向本地仓库配置,打开D:\ProgramFile\Maven\apache-maven-3.5.2\conf下的settings.xml,设置本地仓库地址,如下图:
Bug生活2048
2018/08/31
6990
Spring Boot学习笔记(二)Windows下IDEA 配置Maven
Maven入门手记
1. 下载Maven 下载地址 apache-maven-3.6.0 2. Maven到D盘根目录 3. 配置java环境变量 新建系统变量JAVA_HOME CALSSPATH 变量名:JAVA_HOME 变量值:(你的java解压路劲,到bin目录的上一级) 变量名:CLASSPATH 变量值:.;%JAVA_HOME%\bin\dt.jar;%JAVA_HOME%\tools.jar;%JAVA_HOME%\bin 变量名:Path 变量值:在系统变量Parh后直接加上 ;%JAVA_HOME%\b
暖月寒星
2020/03/11
3930
【安装指南】maven下载、安装与配置详细教程
总体而言,Maven是一个广泛使用的构建工具,它提供了一种简单的方式来管理项目的构建、依赖和发布。通过采用约定优于配置的理念,Maven使得项目构建过程更加标准化和易于维护。
SarPro
2024/02/20
3.8K0
【安装指南】maven下载、安装与配置详细教程
Maven 快速入门
Maven是一个Java工具,因此你的电脑上必须安装有JAVA环境(JDK或者JRE)
大江小浪
2018/07/24
4810
Maven 快速入门
Maven最全最细教程
https://www.cnblogs.com/hzg110/p/6936101.html
KEN DO EVERTHING
2019/04/24
8540
Maven最全最细教程
Maven的基本安装与使用
Maven 翻译为"专家"、"内行",是 Apache 下的一个纯 Java 开发的开源项目。基于项目对象模型(缩写:POM)概念,Maven利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤。Maven 是一个项目管理工具,可以对 Java 项目进行构建、依赖管理。简单来说,Maven是一个框架,可以简化程序员的开发
ma布
2024/10/21
2370
Maven的基本安装与使用
走进Maven世界
本人是Mac OS系统,所以这里介绍Mac下如何安装,在安装Maven之前,先确认已经安装了JDK,其他系统安装也差不多,只是环境变量配置不一样。
Java极客技术
2022/12/04
2890
走进Maven世界
Maven环境配置-必会
想要安装Maven需要下载maven压缩包,无需安装,在windows下只需要配置windows环境变量。
手撕代码八百里
2020/07/28
9030
Maven环境配置-必会
Maven环境配置及介绍[通俗易懂]
下载地址:http://maven.apache.org/downloa/d.cgi
全栈程序员站长
2022/08/28
1.1K0
Maven环境配置及介绍[通俗易懂]
Maven学习笔记之Maven入门
本文涉及Maven知识点有Maven安装与配置,Maven项目结构,Maven依赖管理,Maven项目生命周期管理,基于IDE的Maven使用和Maven私服搭建。
Jetpropelledsnake21
2022/03/07
6950
Maven学习笔记之Maven入门
从头到尾手把手教你搭建阅读Mybatis源码的环境(程序员必备技能)
先放上编译好的mybatis: https://github.com/truedei/mybatis-notes
手撕代码八百里
2021/04/20
1.1K2
从头到尾手把手教你搭建阅读Mybatis源码的环境(程序员必备技能)
myeclipse10配置maven和一些常用命令
Exception in thread "main" java.lang.UnsupportedClassVersionError: org/apache/ma ven/cli/MavenCli : Unsupported major.minor version 51.0 at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631) at java.lang.ClassLoader.defineClass(ClassLoader.java:615) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:14 1) at java.net.URLClassLoader.defineClass(URLClassLoader.java:283) at java.net.URLClassLoader.access$000(URLClassLoader.java:58) at java.net.URLClassLoader$1.run(URLClassLoader.java:197) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:190) at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClassFromSelf(Cl assRealm.java:401) at org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy.loadClass( SelfFirstStrategy.java:42) at org.codehaus.plexus.classworlds.realm.ClassRealm.unsynchronizedLoadCl ass(ClassRealm.java:271) at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm .java:254) at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm .java:239) at org.codehaus.plexus.classworlds.launcher.Launcher.getMainClass(Launch er.java:144) at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Laun cher.java:266) at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.jav a:229) at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(La uncher.java:415) at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java: 356)
用户5640963
2019/07/26
5140
myeclipse10配置maven和一些常用命令
想自学Maven?推荐Maven极简高速入门及常规使用
笔者的工作经历中使用过Ant、Maven和Gradle,如果让笔者选择,笔者会首选Gradle。那为什么还要写Maven这篇内容呢?时下大部分公司都还在使用Maven,并且很多开源项目也在使用Maven,Maven有一些功能特性还是很不错的,很值得做总结记录。
JavaQ
2020/11/09
1.3K0
想自学Maven?推荐Maven极简高速入门及常规使用
Maven的安装与配置以及注意事项
目录 1.Maven的简单介绍  2. 什么是自动构建工具? 3. 常用的自动构建工具 4. Maven安装与配置 5. Maven的使用1:创建java项目 6. Maven的使用2:创建web项目 7. maven打包: 8. 什么是POM 9.注意事项 ---- 1.Maven的简单介绍  Apache Maven是个项目管理和自动构建工具,基于项目对象模型(POM)的概念。 作用:完成项目的相关操作,如:编译,构建,单元测试,安装,网站生成和基于Maven部署项目。  2. 什么是自动构建
天蝎座的程序媛
2022/11/18
5320
IDEA配置Maven教程
本文介绍在IDEA中配置Maven,及其配置settings idea配置manven等功能;
默 语
2024/11/20
1870
IDEA配置Maven教程
一小时教你学会 Maven 项目的构建与管理(1)
Maven为Java开发者提供了一个免费的中央仓库,其中几乎可以找到任何流行的开源类库,通过Maven的衍生工具Nexus,可以进行快速的搜索。Maven项目目录结构有约定的规则,约定优于配置(Convention Over Configuration)。
IT小马哥
2020/03/18
1.6K2
Maven基本知识
​ 将 apache-maven-3.5.0-bin.zip 解压到一个非中文无空格的目录下。 例如:
OY
2022/03/17
7260
Maven基本知识
【愚公系列】2023年03月 Java教学课程 121-Maven的概念与基本使用
Maven是一款用于Java项目管理和构建的工具,它可以自动化构建过程,包括编译、测试、打包、发布等。Maven提供了一种标准化的项目结构和管理方式,可以帮助开发者更好地管理依赖关系、版本控制、构建和部署等方面的问题。Maven使用XML文件来描述项目的构建过程,以及依赖关系和其他相关配置信息。它是Java社区中使用最广泛的构建工具之一。
愚公搬代码
2023/04/06
8710
【愚公系列】2023年03月 Java教学课程 121-Maven的概念与基本使用
Maven使用速记 原
当版本号使用-SNAPSHOT结构的后缀时表示这是一个快照版本。快照版本一般用于开发分支,快照版本即使不改变版本号也会自动获取到最新版本。以只相对的是发布(Release)版本,只要不使用-SNAPSHOT结尾的都是发布版本。每一个发布版本只对应一个编号。
随风溜达的向日葵
2019/05/15
5280
Apache Maven 最全教程,7000 字总结!
前言:目前所有的项目都在使用maven,可是一直没有时间去整理学习,这两天正好有时间,好好的整理一下。
芋道源码
2019/07/05
1.5K0
Apache Maven 最全教程,7000 字总结!
相关推荐
Spring Boot学习笔记(二)Windows下IDEA 配置Maven
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验