Java多线程编程-(9)-使用线程池实现线程的复用和一些坑的避免

线程复用:线程池

首先举个例子:

假设这里有一个系统,大概每秒需要处理5万条数据,这5万条数据为一个批次,而这没秒发送的5万条数据数据需要经过两个处理过程,第一步是数据存入数据库,第二步是对数据进行其他业务的分析,假设第一步我是用的是普通的JDBC插入数据,为了不影响程序的继续执行,我写了一个线程,让这个子线程不阻塞主线程,继续处理第二步骤的数据,我们知道插入5万条数据大概需要2至3秒的时间,如果每一批次插入数据库的时候,就创建一个线程进行处理,可想而知,由于插入数据库的时间较久,不能很快的处理,这样的话,一段时间之后,系统中就会有很多的这种插入数据的线程(PS:只是假设场景,方案设计的可能不合理)。

如果,我们使用上述的方式去创建线程,使用start()方法启动线程,该线程会在run()方法结束后,自动回收该线程。虽然如此,在上边的场景中线程中业务的处理速度完全达不到我们的要求,系统中的线程会逐渐变大,进而消耗CPU资源,大量的线程抢占宝贵的内存资源,可能还会出现OOM,即便没有出现,大量的线程回收也会个GC带来很大的压力。

可想而知,虽然多线程技术可以充分发挥多核处理器的计算能力,提高生产系统的吞吐量和性能。但是,若不加控制和管理的随意使用线程,对系统的性能反而会产生不利的影响。

还拿上边的例子说,如果我们使用线程池的方式的话,可以实现最多创建爱你线程的数量,这样的话就算再多的数据需要入库,只需要排队等待线程池的线程即可,就不会出现线程池过多而消耗系统资源的情况,当然这只是意见简单的场景。

说到这里,有人要说了线程不是携带资源的最小单位,操作系统的书籍中还给我们说了线程之间的切换消耗很小吗?虽然如此,线程是一种轻量级的工具(或者称之为:轻量级进程),但其创建和关闭依然需要花费时间,如果为了一个很简单的任务就去创建一个线程,很有可能出现创建和销毁线程所占用的时间大于该线程真实工作所消耗的时间,反而得不偿失。

那么什么是线程池?

为了避免系统频繁的创建和销毁线程,我们可以将创建的线程进行复用。数据库中的数据库连接池也是此意。

在线程池中总有那么几个活跃的线程,也有一定的最大值限制,一个业务使用完线程之后,不是立即销毁而是将其放入到线程池中,从而实现线程的复用。简而言之:创建线程变成了从线程池获取空闲的线程,关闭线程变成了向池子中归还线程。

再多的概念,不过多解释,因为很基础,也不是本文的重点。

JDK对线程池的支持

JDK提供的Eexecutor框架

JDK提供了Executor框架,可以让我们有效的管理和控制我们的线程,其实质也就是一个线程池。Executor下的接口和类继承关系如下:

其中,ExecutorService接口定义如下:

如果使用Executor框架的话,Executors类是常用的,其方法如下:

其中常用几类如下:

1、newFixedThreadPool:该方法返回一个固定线程数量的线程池;

2、newSingleThreadExecutor:该方法返回一个只有一个现成的线程池;

3、newCachedThreadPool:返回一个可以根据实际情况调整线程数量的线程池;

4、newSingleThreadScheduledExecutor:该方法和newSingleThreadExecutor的区别是给定了时间执行某任务的功能,可以进行定时执行等;

5、newScheduledThreadPool:在4的基础上可以指定线程数量。

创建线程池是指调用的还是ThreadPoolExecutor

在Executors类中,我们拿出来一个方法简单分析一下:

可以看出,类似的其他方法一样,在Executors内部创建线程池的时候,实际创建的都是一个ThreadPoolExecutor对象,只是对ThreadPoolExecutor构造方法,进行了默认值的设定。ThreadPoolExecutor的构造方法如下:

参数含义如下:

Eexecutor框架实例

1、实例一:

submit(Runnable task)方法提交一个线程。

但是使用最新的“阿里巴巴编码规范插件”检测一下会发现:

阿里巴巴编码规范插件地址:https://github.com/alibaba/p3c

2、实例二:

遵循阿里巴巴编码规范的提示,示例如下:

或者这样:

3、实例三:

自定义ThreadFactory、自定义线程拒绝策略

更多实例代码,可参考:

https://gitee.com/xuliugen/codes/ta5dbsge0kvhy62qu8li157

使用submit的坑

首先看一下实例:

运行结果:

上述代码,可以看出运行结果为4个,因该是有5个的,但是当i=0的时候,100/0是会报错的,但是日志信息中没有任何信息,是为什么那?如果使用了submit(Runnable task) 就会出现这种情况,任何的错误信息都出现不了!

这是因为使用submit(Runnable task) 的时候,错误的堆栈信息跑出来的时候会被内部捕获到,所以打印不出来具体的信息让我们查看,解决的方法有如下两种:

1、使用execute()代替submit();

运行结果:

2、使用Future

运行结果:

注:查看源代码请点击阅读原文,PC端效果更佳!

本文分享自微信公众号 - Java后端技术(JavaITWork)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-10-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏彭湖湾的编程世界

【算法】论平衡二叉树(AVL)的正确种植方法

《算法(java)》                           — — Robert Sedgewick, Kevin Wayne

18520
来自专栏老马寒门IT

jQuery EasyUI 详解

easyui 为创建现代化,互动,JavaScript 应用程序,提供必要的功能。

72010
来自专栏繁花云

12-01-js深入学习

问题表现是:httpd服务器已经开启了,端口也已经监听了,但是就是无法访问

7900
来自专栏彭湖湾的编程世界

【javascript】谈谈HTML5: Web-Worker、canvas、indexedDB、拖拽事件

前言:作为一名Web开发者,可能你并没有对这个“H5”这个字眼投入太多的关注,但实际上它早已不知不觉进入到你的开发中,并且总有一天会让你不得不正视它,了解它并运...

36830
来自专栏北京马哥教育

国家认证的Python工程师有什么能力要求?

Python这门语言近来是越来越火,在国家层面越来越被重视。除了之前热议的加入高考和中小学教育之外,现在连普通大学生也无法逃脱Python的毒手了。

1K00
来自专栏coder修行路

《深入理解计算机系统》阅读笔记--程序的机器级表示(上)

编译器基于编程语言的规则,目标机器的指令集和操作系统遵循的惯例,经过一系列的阶段生成机器代码。GCC c语言编译器以汇编代码的形式产生输出,汇编代码是机器代码的...

14500
来自专栏彭湖湾的编程世界

【算法】哈希表的诞生

《算法(java)》                           — — Robert Sedgewick, Kevin Wayne

17370
来自专栏繁花云

巧用js替换某些不能替换的文字

在运行某些程序时,作者为了保护版权,将版权文字进行了特殊处理,使得我们无法进行修改。

18300
来自专栏软件开发

JavaScript学习总结(五)——jQuery插件开发与发布

jQuery插件就是以jQuery库为基础衍生出来的库,jQuery插件的好处是封装功能,提高了代码的复用性,加快了开发速度,现在网络上开源的jQuery插件非...

13630
来自专栏极乐技术社区

小程序一周报 | 新注册公众号将没有留言功能

微信团队为进一步规范公众平台生态环境,后续新注册的账号将没有留言功能,「最近三个月内注册,但尚未使用留言功能的账号将被收回留言权限。」

18500

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励