Java线程池的那些事

熟悉java多线程的朋友一定十分了解java的线程池,jdk中的核心实现类为java.util.concurrent.ThreadPoolExecutor。大家可能了解到它的原理,甚至看过它的源码;但是就像我一样,大家可能对它的作用存在误解。现在问题来了,jdk为什么要提供java线程池?使用java线程池对于每次都创建一个新Thread有什么优势?

对线程池的误解

很长一段时间里我一直以为java线程池是为了提高多线程下创建线程的效率。创建好一些线程并缓存在线程池里,后面来了请求(Runnable)就从连接池中取出一个线程处理请求;这样就避免了每次创建一个新Thread对象。直到前段时间我看到一篇Neal Gafter(和Joshua Bloch合著了《Java Puzzlers》,现任职于微软,主要从事.NET语言方面的工作)的访谈,里面有这么一段谈话(http://www.infoq.com/cn/articles/neal-gafter-on-java):

乍一看,大神的思路就是不一样:java线程池是为了防止java线程占用太多资源?

虽然是java大神的访谈,但是也不能什么都信,你说占资源就占资源?还是得写测试用例测一下。

首先验证下我的理解:

java线程池和创建java线程哪个效率高?

直接上测试用例:

这里使用Executors.newFixedThreadPool(100)是为了控制线程池的核心连接数和最大连接数一样大,都为100。

我的机子上的测试结果:

testThreadPool:31
testNewThread:624

可以看到,使用线程池处理10000个请求的处理时间为31ms,而每次启用新线程的处理时间为624ms。

好了,使用线程池确实要比每次都创建新线程要快一些;但是testNewThread一共耗时624ms,算下平均每次请求的耗时为:

624ms/10000=62.4us

每次创建并启动线程的时间为62.4微秒。根据80/20原理,这点儿时间根本可以忽略不计。所以线程池并不是为了效率设计的。

java线程池是为了节约资源?

再上测试用例:

以上用例模拟每次请求都创建一个新线程处理请求,然后默认每个请求的处理时间为1000ms。而在我的机子上当请求数达到1096时会内存溢出:

java.lang.OutOfMemoryError: unable to create new native thread

为什么会抛OOM Error呢?因为jvm会为每个线程分配一定内存(JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K,也可以通过jvm参数-Xss来设置),所以当线程数达到一定数量时就报了该error。

设想如果不使用java线程池,而为每个请求都创建一个新线程来处理该请求,当请求量达到一定数量时一定会内存溢出的;而我们使用java线程池的话,线程数量一定会<=maximumPoolSize(线程池的最大线程数),所以设置合理的话就不会造成内存溢出。

现在问题明朗了:java线程池是为了防止内存溢出,而不是为了加快效率。

浅谈java线程池

上文介绍了java线程池启动太多会造成OOM,使用java线程池也应该设置合理的线程数数量;否则应用可能十分不稳定。然而该如何设置这个数量呢?我们可以通过这个公式来计算:

(MaxProcessMemory – JVMMemory – ReservedOsMemory) / (ThreadStackSize) = Max number of threads

  • MaxProcessMemory 进程最大的内存
  • JVMMemory JVM内存
  • ReservedOsMemory JVM的本地内存
  • ThreadStackSize 线程栈的大小

MaxProcessMemory

MaxProcessMemory:进程最大的寻址空间,当然也不能超过虚拟内存和物理内存的总和。关于不同系统的进程可寻址的最大空间,可参考下面表格:

Maximum Address Space Per Process

Operating System

Maximum Address Space Per Process

Redhat Linux 32 bit

2 GB

Redhat Linux 64 bit

3 GB

Windows 98/2000/NT/Me/XP

2 GB

Solaris x86 (32 bit)

4 GB

Solaris 32 bit

4 GB

Solaris 64 bit

Terabytes

JVMMemory

JVMMemory: Heap + PermGen,即堆内存和永久代内存和(注意,不包括本地内存)。

ReservedOsMemory

ReservedOSMemory:Native heap,即JNI调用方法所占用的内存。

ThreadStackSize

ThreadStackSize:线程栈的大小,JDK5.0以后每个线程堆栈大小默认为1M,以前每个线程堆栈大小为256K;可以通过jvm参数-Xss来设置;注意-Xss是jvm的非标准参数,不强制所有平台的jvm都支持。

如何调大线程数?

如果程序需要大量的线程,现有的设置不能达到要求,那么可以通过修改MaxProcessMemory,JVMMemory,ThreadStackSize这三个因素,来增加能创建的线程数:

  • MaxProcessMemory 使用64位操作系统
  • JVMMemory 减少JVMMemory的分配
  • ThreadStackSize 减小单个线程的栈大小

原文发布于微信公众号 - java一日一条(mjx_java)

原文发表时间:2016-05-13

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏闵开慧

让XP支持4G内存

  4G内存已经成为很多中高档笔记本电脑的标准配置。前两篇文章介绍了WIN7系统4G内存的破解过程和4G内存的完美利用。但是,很多朋友还是习惯用32位 XP系统...

1.6K110
来自专栏LanceToBigData

IDEA将项目上传至码云/GitHub托管

前言   好久都没有写博客了,由于博主之前一直都在上班处于加班的阶段,所以根本就没有时间去学习。现在请假回到学校写论文,有时间来学习了。   所以会不断的进行博...

52170
来自专栏Crossin的编程教室

把你开发的网站免费发布到互联网上(2)

前几天,我们介绍了通过 PythonAnywhere 在互联网上创建一个站点: 把你开发的网站免费发布到互联网上(1) 本篇是上一篇的延续,来讲一讲如何将已有的...

39190
来自专栏杨建荣的学习笔记

Datapump数据迁移前的准备工作(r9笔记第31天)

其实对于Datapump迁移而言,如果参与过XTTS,OGG,Veritas SF,外部表增量等迁移方式的话,会发现Datapump还是很简单清晰的,一个优点...

28230
来自专栏张戈的专栏

解决启用WP-Super-Cache后出现的几个问题

近期,随着新版互推联盟自适应 iframe 代码的推出,调用的博友也慢慢增加了 ,这是很高兴的事情,也有博友反应调用的这个页面加载会有点慢。我来说明一下,因为这...

44160
来自专栏同步博客

我对RPC的理解

  这篇文章将会用最直白的方式介绍RPC,以及实现RPC客户端的Ajax跨域调用的例子。

16840
来自专栏pangguoming

Ubuntu 14.04 分区方案

我磁盘大概还有70多G的空间吧,我全部拿来使用的。真实的双系统哦。 一般来讲,linux系统分区最少要包括/和/swap两个。这样据说会影响性能,没有这样安装过...

44460

如何在Linux上搭建Terraria 游戏服务器?

Terraria是一款二维沙盒游戏,类似于Minecraft(我的世界),允许玩家在开放的世界中探索,构建和战斗。2015年,Terraria开发者宣布支持Li...

85130
来自专栏腾讯移动品质中心TMQ的专栏

React Native框架如何白盒测试-HIPPY接口测试架构篇

本接口测试框架适合Android拥有前端、终端交互的APP接口测试之用。

66660
来自专栏数据和云

性能优化:Linux环境下合理配置大内存页

熊军(老熊) 云和恩墨西区总经理 Oracle ACED,ACOUG核心会员 PC Server发展到今天,在性能方面有着长足的进步。64位的CPU在数年前都已...

92450

扫码关注云+社区

领取腾讯云代金券