sap java开发技术详解&mdash基础

94.01元

(需用券)

去购买 >

针对 IO 密集型的任务,我们可以针对原本的线程池做一些改造,从而可以提高任务的处理效率。

基本

在阿里巴巴泰山版java开发手册中有这么一条:

线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,

这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

那么如果要使用 ThreadPoolExecutor ,那就先来看看构造方法中的所有入参:

corePoolSize : 核心线程数,当线程池中的线程数量为 corePoolSize 时,即使这些线程处于空闲状态,也不会销毁(除非设置 allowCoreThreadTimeOut)。

maximumPoolSize : 最大线程数,线程池中允许的线程数量的最大值。

keepAliveTime : 线程空闲时间,当线程池中的线程数大于 corePoolSize 时,多余的空闲线程将在销毁之前等待新任务的最长时间。

workQueue : 任务队列

unit : 线程空闲时间的单位。

threadFactory : 线程工厂,线程池创建线程时使用的工厂。

handler : 拒绝策略,因达到线程边界和任务队列满时,针对新任务的处理方法。

这么说可能有些难以理解,你可以结合下图进行参考:

那么由此我们可以知道,当大量任务被放入线程池之后,先是被核心线程执行,多余的会被放进队列里,当队列满了之后才会创建额外的线程进行处理,再多就会采取拒绝策略。

但这样真的能满足我们的所有需求吗?

任务的分类

正常来说,我们可以把需要处理的任务按照消耗资源的不同,分为两种:CPU 密集型和IO 密集型。

CPU 密集型

既然名字里带有CPU了,说明其消耗的主要资源就是 CPU 了。

具体是指那种包含大量运算、在持有的 CPU 分配的时间片上一直在执行任务、几乎不需要依赖或等待其他任何东西。

这样的任务,在我的理解中,处理起来其实没有多少优化空间,因为处理时几乎没有等待时间,所以一直占有 CPU 进行执行,才是最好的方式。

唯一能想到优化的地方,就是当单个线程累计较多任务时,其他线程能进行分担,类似fork/join框架的概念。

设置线程数时,针对单台机器,最好就是有几个 CPU ,就创建几个线程,然后每个线程都在执行这种任务,永不停歇。

IO 密集型

和上面一样,既然名字里带有IO了,说明其消耗的主要资源就是 IO 了。

我们所接触到的 IO ,大致可以分成两种:磁盘 IO和网络 IO。

磁盘 IO ,大多都是一些针对磁盘的读写操作,最常见的就是文件的读写,假如你的数据库、 Redis 也是在本地的话,那么这个也属于磁盘 IO。

网络 IO ,这个应该是大家更加熟悉的,我们会遇到各种网络请求,比如 http 请求、远程数据库读写、远程 Redis 读写等等。

IO 操作的特点就是需要等待,我们请求一些数据,由对方将数据写入缓冲区,在这段时间中,需要读取数据的线程根本无事可做,因此可以把 CPU 时间片让出去,直到缓冲区写满。

既然这样,IO 密集型任务其实就有很大的优化空间了(毕竟存在等待),那现有的线程池可以很好的满足我们的需求吗?

线程池的优化

还记得上面说的, ThreadPoolExecutor 针对多余任务的处理,是先放到等待队列中,当队列塞满后,再创建额外的线程进行处理。

假设我们的任务基本都是 IO 密集型,我们希望程序可以有更高的吞吐量,可以在更短的时间内处理更多的任务,那么上面的 ThreadPoolExecutor 明显是不满足我们的需求,那该如何解决呢?

也许再来看看 ThreadPoolExecutor 的 execute 方法,会让我们有一些思路:

public void execute(Runnable command) {

if (command == null)

throw new NullPointerException();

int c = ctl.get();

// 如果当前活跃线程数,小于核心线程数

if (workerCountOf(c) < corePoolSize) {

// 则优先创建线程

if (addWorker(command, true))

return;

c = ctl.get();

}

// 如果任务可以成功放入队列中

if (isRunning(c) && workQueue.offer(command)) {

int recheck = ctl.get();

if (! isRunning(recheck) && remove(command))

reject(command);

else if (workerCountOf(recheck) == 0)

addWorker(null, false);

}

// 如果不可以成功放入队列,则创建线程

else if (!addWorker(command, false))

// 如果无法继续创建线程,则拒绝任务

reject(command);

}

针对放入队列的操作,如果队列放入失败,线程池就会选择去创建线程了。因此,我们或许可以尝试自定义线程池,针对 offer 操作,做一些自定义处理。

也就是将任务放入队列时,先检查线程池的线程数是否小于最大线程数,如果是,则拒绝放入队列,否则,再尝试放入队列中。

如果你有看过 dubbo 或者 tomcat 的线程池,你会发现他们就有这样的实现方法。

比如 dubbo 中的 TaskQueue,我们来看看它的 offer 方法:

@Override

public boolean offer(Runnable runnable) {

if (executor == null) {

throw new RejectedExecutionException("The task queue does not have executor!");

}

int currentPoolThreadSize = executor.getPoolSize();

// 如果有空闲等待的线程,则将任务放入队列中,让线程去处理任务

if (executor.getSubmittedTaskCount() < currentPoolThreadSize) {

return super.offer(runnable);

}

// 如果当前线程数小于最大线程数,则返回 false ,让线程池去创建新的线程

if (currentPoolThreadSize < executor.getMaximumPoolSize()) {

return false;

}

// 否则,就将任务放入队列中

return super.offer(runnable);

}

这样就可以让线程池优先新建线程了。需要注意的时,此时的队列因为需要根据线程池中的线程数决定是否放入任务成功,所以需要持有executor对象,这点不要忘记奥。

总结

通过本篇文章,主要是让大家重新了解了一下 ThreadPoolExecutor ,并针对高吞吐场景下如何进行局部优化。

有兴趣的话可以访问我的博客或者关注我的公众号、头条号,说不定会有意外的惊喜。

https://death00.github.io/

公众号:健程之道

java 11官方入门(第8版)教材

79.84元

包邮

(需用券)

去购买 >

java io密集型任务_Java线程池讲解——针对IO密集型任务相关推荐

  1. java assert使用场景_Java线程池的四种用法与使用场景

    一.如下方式存在的问题 new Thread() { @Override public void run() { // 业务逻辑 }}.start(); 1.首先频繁的创建.销毁对象是一个很消耗性能的 ...

  2. java new thread参数_java线程池01-ThreadPoolExecutor构造方法参数的使用规则

    为了更好的使用多线程,JDK提供了线程池供开发人员使用,目的在于减少线程的创建和销毁次数,以此达到线程的重复利用. 其中ThreadPoolExecutor是线程池中最核心的一个类,我们先简单看一下这 ...

  3. java多线程 占用内存_java线程池常驻线程占内存吗

    看了下代码: public void run() { try { Runnable task = firstTask; firstTask = null; while (task != null || ...

  4. java executor 源码_Java线程池ThreadPoolExecutor深度探索及源码解析

    我们的程序里,时常要使用多线程.因此多线程的管理变的尤为重要.ThreadPoolExecutor很好的解决了这一点.本篇文章主要从源码入手,分析ThreadPoolExecutor的原理. 1.标记 ...

  5. java线程池概念_Java 线程池概念、原理、简单实现

    线程池的思想概述 我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结東了,这样频繁创建线程就会大大降低系 ...

  6. java线程优先级队列等待_java线程池队列优先级(插队)Demo

    在做线程池操作的时候,突然来个加紧处理时,会很纠结,不知道怎么处理让加紧的线程插队先执行.该Demo使用了自定义线程池,采用优先级阻塞式队列(PriorityBlockingQueue)的方式来处理插 ...

  7. Java多线程系列(五):线程池的实现原理、优点与风险、以及四种线程池实现

    为什么需要线程池 我们有两种常见的创建线程的方法,一种是继承Thread类,一种是实现Runnable的接口,Thread类其实也是实现了Runnable接口.但是我们创建这两种线程在运行结束后都会被 ...

  8. Java番外篇3——线程池

    Java番外篇3--线程池 1.多线程产生的问题 多次创建并销毁线程.而创建并销毁线程的过程势必会消耗内存 2.线程池 降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗 提高系统响 ...

  9. 码出高效:Java开发手册笔记(线程池及其源码)

    码出高效:Java开发手册笔记(线程池及其源码) 码出高效:Java开发手册笔记(线程池及其源码) 码出高效:Java开发手册笔记(线程池及其源码) 前言 一.线程池的作用 线程的生命周期 二.线程池 ...

最新文章

  1. 为何 epoll 的 ET 模式一定要设置为非阻塞IO
  2. modbus-tcp qt4-socket ---------micro2440 as device
  3. Silverlight中枚举并加载客户端程序集
  4. 解析并符号 读取dll_风电场用风功率采集测风塔数据报文格式解析浅谈
  5. rust如何在木板上上传图片_通过编写一个简单的游戏来学习 Rust | Linux 中国
  6. 如何在linux查找虚拟机主机号_Linux主机名如何重命名?
  7. NLP算法求建议 | 腾讯 VS 美团
  8. 分窗 Gram-Schmidt 高光谱降维的 水稻纹枯病检测
  9. template模板函数
  10. CS61C 学习笔记 --实时更新
  11. 软件测试详细的基本流程
  12. 【雷达】基于粒子群算法优化综合微带天线阵列方向图附matlab代码
  13. MongoDB+Node.js+express简单实现数据的提交与回显
  14. matplotlib中关于极坐标轴的控制
  15. 闪电网络如何实现更加去中心化的网络
  16. 鲑鱼和金枪鱼罐头的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  17. python常用数据作图--matplotlib用法(相关设置及常用图)
  18. 终于拿到蚂蚁金服Offer!!!分享一下全程面试题和面试经验!
  19. 计算机操作系统实验指导linux版,操作系统实验指导书(linux版).doc
  20. uniapp微信小程序富文本编辑器组件

热门文章

  1. python程序把文件编码转换
  2. 具备安全态势感知能力的安全管理平台
  3. Alexa 调整网站排名统计方式
  4. spring整合mybatis是如何配置事务的?
  5. 在SQL SERVER中实现Split功能的函数,并在存储过程中使用
  6. Dojo API略解续
  7. PHP垃圾回收深入理解
  8. Git多个commit合并成一个【中间提交合并 尾部提交合并】
  9. java http2_java的okhttp3库中,客户端如何开启http2协议支持
  10. PHP图片上传,框架通用