前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java线程池详解

Java线程池详解

作者头像
公众号 IT老哥
修改2020-09-21 15:06:53
8530
修改2020-09-21 15:06:53
举报

本文源自 公-众-号 IT老哥 的分享

线程池的必要性及作用

线程能够充分合理地协调利用CPU、内存、I/O等系统资源,但是线程的创建需要开辟虚拟机栈、本地方法栈、程序计数器等线程私有空间,在线程销毁时需要回收这些系统资源。频繁地创建和销毁线程会大大浪费系统资源,这时候就需要线程池来管理线程,提高线程的复用(当然线程的作用并不仅于此)。

线程的作用:

复用线程、控制最大并发数。实现定时、周期等与时间相关的功能。实现任务队列缓存策略和拒绝机制。隔离线程环境。如:文件上传服务和数据查询服务在同一台服务器上,由于文件上传服务耗时严重,如果文件上传和数据查询服务使用同一个线程池,那么文件上传服务会影响到数据查询服务。可以通过配置独立线程池来实现文件上传和数据查询服务隔离,避免两者相互影响。 如何创建线程池

JDK中提供了创建线程池的类,大家首先想到的一定是Executors类,没错,可以通过Executors类来创建线程池,但是不推荐(原因后面会分析)。Executors类只是个静态工厂,提供创建线程池的几个静态方法(内部屏蔽了线程池参数配置细节),而真正的线程池类是ThreadPoolExecutor。ThreadPoolExecutor构造方法如下:

参数解释:

corePoolSize:核心线程数。如果等于0,则任务执行完后,没有任务请求进入时销毁线程池中的线程。如果大于0,即使本地任务执行完毕,核心线程也不会被销毁。设置过大会浪费系统资源,设置过小导致线程频繁创建。maximumPoolSize:最大线程数。必须大于等于1,且大于等于corePoolSize。如果与corePoolSize相等,则线程池大小固定。如果大于corePoolSize,则最多创建maximumPoolSize个线程执行任务,其他任务加入到workQueue缓存队列中,当workQueue为空且执行任务数小于maximumPoolSize时,线程空闲时间超过keepAliveTime会被回收。keepAliveTime:线程空闲时间。线程池中线程空闲时间达到keepAliveTime值时,线程会被销毁,只到剩下corePoolSize个线程为止。默认情况下,线程池的最大线程数大于corePoolSize时,keepAliveTime才会起作用。如果allowCoreThreadTimeOut被设置为true,即使线程池的最大线程数等于corePoolSize,keepAliveTime也会起作用(回收超时的核心线程)。unit:TimeUnit表示时间单位。workQueue:缓存队列。当请求线程数大于maximumPoolSize时,线程进入BlockingQueue阻塞队列。threadFactory:线程工厂。用来生产一组相同任务的线程。主要用于设置生成的线程名词前缀、是否为守护线程以及优先级等。设置有意义的名称前缀有利于在进行虚拟机分析时,知道线程是由哪个线程工厂创建的。handler:执行拒绝策略对象。当达到任务缓存上限时(即超过workQueue参数能存储的任务数),执行拒接策略,可以看作简单的限流保护。线程池相关类结构

ExecutorService接口继承了Executor接口,定义了管理线程任务的方法。ExecutorService的抽象类AbstractExecutorService提供了submit、invokeAll()等部分方法实现,但是核心方法Executor.execute()并没有实现。因为所有任务都在这个方法里执行,不同的线程池实现策略会有不同,所以交由具体的线程池来实现。

Executors核心方法

Executors.newFixedThreadPool:创建固定线程数的线程池。核心线程数等于最大线程数,不存在空闲线程,keepAliveTime为0。

Executors.newSingleThreadExecutor:创建单线程的线程池,核心线程数和最大线程数都为1,相当于串行执行。

Executors.newScheduledThreadPool:创建支持定时以及周期性任务执行的线程池。最大线程数是Integer.MAX_VALUE。存在OOM风险。keepAliveTime为0,所以不回收工作线程。

Executors.newCachedThreadPool:核心线程数为0,最大线程数为Integer.MAX_VALUE,是一个高度可伸缩的线程池。存在OOM风险。keepAliveTime为60,工作线程处于空闲状态超过keepAliveTime会回收线程。

Executors.newWorkStealingPool:JDK8引入,创建持有足够线程的线程池支持给定的并行度,并通过使用多个队列减少竞争。

禁止直接使用Executors创建线程池原因:

Executors.newCachedThreadPool和Executors.newScheduledThreadPool两个方法最大线程数为Integer.MAX_VALUE,如果达到上限,没有任务服务器可以继续工作,肯定会抛出OOM异常。

Executors.newSingleThreadExecutor和Executors.newFixedThreadPool两个方法的workQueue参数为new LinkedBlockingQueue<Runnable>(),容量为Integer.MAX_VALUE,如果瞬间请求非常大,会有OOM风险。

总结:以上5个核心方法除Executors.newWorkStealingPool方法之外,其他方法都有OOM风险。

如何自定义ThreadFactory

如上代码所示,实现ThreadFactory接口并在newThread方法中实现设置线程的名称、是否为守护线程以及线程优先级等属性。这样做有助于快速定位死锁、StackOverflowError等问题。如下图所示,绿色框自定义的线程工厂明显比蓝色的默认线程工厂创建的线程名称拥有更多的额外信息。

自定义线程工厂线程信息打印对比图

线程拒绝策略

ThreadPoolExecutor提供了四个公开的内部静态类:

AbortPolicy:默认,丢弃任务并抛出RejectedExecutionException异常。DiscardPolicy:丢弃任务,但是不抛出异常(不推荐)。DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中。CallerRunsPolicy:调用任务的run()方法绕过线程池直接执行。友好的拒绝策略:

保存到数据库进行削峰填谷。在空闲时再提出来执行。转向某个提示页面打印日志自定义拒绝策略:

云服务器云硬盘数据库(包括MySQL、Redis、MongoDB、SQL Server),CDN流量包,短信流量包,cos资源包,消息队列ckafka,点播资源包,实时音视频套餐,网站管家(WAF),大禹BGP高防(包含高防包及高防IP),云解析SSL证书,手游安全MTP移动应用安全云直播等等。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT老哥 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 本文源自 公-众-号 IT老哥 的分享
相关产品与服务
实时音视频
实时音视频(Tencent RTC)基于腾讯21年来在网络与音视频技术上的深度积累,以多人音视频通话和低延时互动直播两大场景化方案,通过腾讯云服务向开发者开放,致力于帮助开发者快速搭建低成本、低延时、高品质的音视频互动解决方案。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档