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线程哪个效率高?
直接上测试用例:
public class ThreadPoolTest extends TestCase {
private static final int COUNT = 10000;
public void testThreadPool() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(COUNT);
ExecutorService executorService = Executors.newFixedThreadPool(100);
long bg = System.currentTimeMillis();
for (int i = 0; i < COUNT; i++) {
Runnable command = new TestRunnable(countDownLatch);
executorService.execute(command);
}
countDownLatch.await();
System.out.println("testThreadPool:" + (System.currentTimeMillis() - bg));
}
public void testNewThread() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(COUNT);
long bg = System.currentTimeMillis();
for (int i = 0; i < COUNT; i++) {
Runnable command = new TestRunnable(countDownLatch);
Thread thread = new Thread(command);
thread.start();
}
countDownLatch.await();
System.out.println("testNewThread:" + (System.currentTimeMillis() - bg));
}
private static class TestRunnable implements Runnable {
private final CountDownLatch countDownLatch;
TestRunnable(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
countDownLatch.countDown();
}
}
}
这里使用Executors.newFixedThreadPool(100)是为了控制线程池的核心连接数和最大连接数一样大,都为100。
我的机子上的测试结果:
testThreadPool:31
testNewThread:624
可以看到,使用线程池处理10000个请求的处理时间为31ms,而每次启用新线程的处理时间为624ms。
好了,使用线程池确实要比每次都创建新线程要快一些;但是testNewThread一共耗时624ms,算下平均每次请求的耗时为:
624ms/10000=62.4us
每次创建并启动线程的时间为62.4微秒。根据80/20原理,这点儿时间根本可以忽略不计。所以线程池并不是为了效率设计的。
java线程池是为了节约资源?
再上测试用例:
public class ThreadPoolTest extends TestCase {
public void testThread() throws InterruptedException {
int i = 1;
while (true) {
Runnable command = new TestRunnable();
Thread thread = new Thread(command);
thread.start();
System.out.println(i++);
}
}
private static class TestRunnable implements Runnable {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
以上用例模拟每次请求都创建一个新线程处理请求,然后默认每个请求的处理时间为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线程池的那些事的更多相关文章
- java线程池的初探
问题来源 发现学习很多技术都提到了线程池的技术,自己的线程池方面没有仔细研究过,现在看了点东西来这里总结下,最近发现写博客是一个很好的锻炼自己并且将学到的东西更加理解的一个方式. 问题探究 java的 ...
- 【转载】深度解读 java 线程池设计思想及源码实现
总览 开篇来一些废话.下图是 java 线程池几个相关类的继承结构: 先简单说说这个继承结构,Executor 位于最顶层,也是最简单的,就一个 execute(Runnable runnable) ...
- Java并发指南12:深度解读 java 线程池设计思想及源码实现
深度解读 java 线程池设计思想及源码实现 转自 https://javadoop.com/2017/09/05/java-thread-pool/hmsr=toutiao.io&utm_ ...
- Java线程池进阶
线程池是日常开发中常用的技术,使用也非常简单,不过想使用好线程池也不是件容易的事,开发者需要不断探索底层的实现原理,才能在不同的场景中选择合适的策略,最大程度发挥线程池的作用以及避免踩坑. 一.线程池 ...
- Java 线程池框架核心代码分析--转
原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...
- Java线程池使用说明
Java线程池使用说明 转自:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极 ...
- (转载)JAVA线程池管理
平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发攻城师却在这个上面吃了不少苦头.怎么做一套简便的线程开发模式 ...
- 四种Java线程池用法解析
本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 http://www.jb51.net/article/81843.htm 1.new Thread的弊端 执行一个异步任务你还只是如下 ...
- Java线程池的几种实现 及 常见问题讲解
工作中,经常会涉及到线程.比如有些任务,经常会交与线程去异步执行.抑或服务端程序为每个请求单独建立一个线程处理任务.线程之外的,比如我们用的数据库连接.这些创建销毁或者打开关闭的操作,非常影响系统性能 ...
随机推荐
- C# FTP上传
public class FtpHelper { public FtpHelper(string p_url, string p_user, string p_password) { if (!p_u ...
- 移动端调试利器 JSConsole 介绍
先看这篇文章 Web应用调试:现在是Weinre和JSConsole,最终会是WebKit的远程调试协议. 我们先不看未来,从此文可见,当下的移动端调试还是 Weinre 和 JSConsole 的天 ...
- FFmpeg 1.2 for Android 生成一个动态库
上一篇<FFmpeg 1.2 for Android 编译动态库>里沃特跟大家介绍了如何编译动态库,但当时所生成的动态库总共包含10个so文件,这样要是加载起来会严重影响软件的启动速度,后 ...
- 利用uploadify+asp.net 实现大文件批量上传。
前言 现在网上文件上传组件随便一搜都是一大堆,不过看大家一般都在用uploadify这个来上传文件.由于项目需要,我在来试了一下.因为第一次使用,也遇到了很多问题,特此记录! ------------ ...
- 刷新SqlServer所有视图【存储过程】
摘自:http://www.cnblogs.com/yashen/archive/2004/12/23/81000.html CREATE PROCEDURE RefreshAllView AS DE ...
- 图片上传和显示——上传图片——上传文件)==ZJ
http://www.cnblogs.com/yc-755909659/archive/2013/04/17/3026409.html aspx上传 http://www.cnblogs.com/mq ...
- STL中algorithm里的查找
首先,选择查找算法时,区间是否排序是一个至关重要的因素.可以按是否需要排序区间分为两组: A. count,find B. binary_search,lower_bound,upper_bound, ...
- 解决"415 Cannot process the message because the content type 'application/x-www-form-urlencoded' was not the expected type 'text/xml; charset=utf-8'"
wcf basicHttpBinding content-type text/xml;charset=utf-8 wsHttpBinding 'application/soap+xml; ch ...
- Beta版本冲刺———第一天
会议照片: 项目燃尽图: 1.项目进展: 昨天的困难:对2048项目中方块颜色的调整 今天解决的进度:调整了方块的颜色,原来用UIColor.(颜色名)color颜色效果不是很好,现在改用了RGB调色 ...
- 1017关于EXPLAIN的语法
转自博客 http://blog.csdn.net/zhuxineli/article/details/14455029 explain显示了MySQL如何使用索引来处理select语句以及连接表.可 ...