[Java Performance] 线程及同步的性能之线程池/ThreadPoolExecutors/ForkJoinPool
- 任务被投放到一个队列中(队列的数量不定)
- 线程从队列中取得任务并执行
- 线程完成任务后,继续尝试从队列中取得任务,如果队列为空,那么线程进入等待状态
线程池往往拥有最小和最大线程数:
- 最小线程数,即当任务队列为空时,线程池中最少需要保持的线程数量,这样做是考虑到创建线程是一个相对耗费资源的操作,应当尽可能地避免,当有新任务被投入队列时,总会有线程能够立即对它进行处理。
- 最大线程数,当需要处理的任务过多时,线程池能够拥有的最大线程数。这样是为了保证不会有过多的线程被创建出来,因为线程的运行需要依赖于CPU资源和其它各种资源,当线程过多时,反而会降低性能。
在ThreadPoolExecutor和其相关的类型中,最小线程数被称为线程池核心规模(Core Pool Size),在其它Java应用服务器的实现中,这个数量也许被称为最小线程数(MinThreads),但是它们的概念是相同的。
- 任务的特征
- 计算机的硬件情况
从上面中得到一些结论:
- 当线程数为4时,达到最优性能,再增加线程数量时并没有更好的性能,因为此时CPU的利用率已经达到了最高,在增加线程只会增加线程之间争夺CPU资源的行为,因此反而降低了性能。
- 即使在CPU利用率达到最高时,基线百分比也不是理想中的25%,这是因为虽然在程序运行过程中,CPU资源并不是只被应用程序线程独享的,一些后台线程有时也会需要CPU资源,比如GC线程和系统的一些线程等。
当计算是通过Servlet触发的时候,性能数据是下面这个样子的(Load Generator会同时发送20个请求):

从上表中可以得到的结论:
- 在线程数量为4时,性能最优。因为此任务的类型是计算密集型的,只有4个CPU,因此线程数量为4时,达到最优情况。
- 随着线程数量逐渐增加,性能下降,因为线程之间会互相争夺CPU资源,造成频繁切换线程执行上下文环境,而这些切换只会浪费CPU资源。
- 性能下降的速度并不明显,这也是因为任务类型是计算密集型的缘故,如果性能瓶颈不是CPU提供的计算资源,而是外部的资源,如数据库,文件操作等,那么增加线程数量带来的性能下降也许会更加明显。
下面,从Client的角度考虑一下问题,并发Client的数量对于Server的响应时间会有什么影响呢?还是同样地环境,当并发Client数量逐渐增加时,响应时间会如下发生变化:

- 有任务需要被执行
- 当前线程池中所有的线程都处于工作状态
- 当前线程池的线程数没有达到最大线程数
至于线程池会如何创建这个新的线程,则是根据任务队列的种类:
- 线程池是对象池的一个有用的例子,它能够节省在创建它们时候的资源开销。并且线程池对系统中的线程数量也起到了很好的限制作用。
- 线程池中的线程数量必须仔细的设置,否则冒然增加线程数量只会带来性能的下降。
- 在定制ThreadPoolExecutor时,遵循KISS原则,通常情况下会提供最好的性能。
ForkJoinPool
public class ForkJoinTest {
private double[] d;
private class ForkJoinTask extends RecursiveTask<Integer> {
private int first;
private int last;
public ForkJoinTask(int first, int last) {
this.first = first;
this.last = last;
}
protected Integer compute() {
int subCount;
if (last - first < 10) {
subCount = 0;
for (int i = first; i <= last; i++) {
if (d[i] < 0.5)
subCount++;
}
}
else {
int mid = (first + last) >>> 1;
ForkJoinTask left = new ForkJoinTask(first, mid);
left.fork();
ForkJoinTask right = new ForkJoinTask(mid + 1, last);
right.fork();
subCount = left.join();
subCount += right.join();
}
return subCount;
}
}
public static void main(String[] args) {
d = createArrayOfRandomDoubles();
int n = new ForkJoinPool().invoke(new ForkJoinTask(0, 9999999));
System.out.println("Found " + n + " values");
}
}
public class ThreadPoolTest {
private double[] d;
private class ThreadPoolExecutorTask implements Callable<Integer> {
private int first;
private int last;
public ThreadPoolExecutorTask(int first, int last) {
this.first = first;
this.last = last;
}
public Integer call() {
int subCount = 0;
for (int i = first; i <= last; i++) {
if (d[i] < 0.5) {
subCount++;
}
}
return subCount;
}
}
public static void main(String[] args) {
d = createArrayOfRandomDoubles();
ThreadPoolExecutor tpe = new ThreadPoolExecutor(4, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new LinkedBlockingQueue());
Future[] f = new Future[4];
int size = d.length / 4;
for (int i = 0; i < 3; i++) {
f[i] = tpe.submit(new ThreadPoolExecutorTask(i * size, (i + 1) * size - 1);
}
f[3] = tpe.submit(new ThreadPoolExecutorTask(3 * size, d.length - 1);
int n = 0;
for (int i = 0; i < 4; i++) {
n += f.get();
}
System.out.println("Found " + n + " values");
}
}
在分别使用ForkJoinPool和ThreadPoolExecutor时,它们处理这个问题的时间如下:

for (int i = first; i <= last; i++) {
if (d[i] < 0.5) {
subCount++;
}
for (int j = 0; j < d.length - i; j++) {
for (int k = 0; k < 100; k++) {
dummy = j * k + i; // dummy is volatile, so multiple writes occur
d[i] = dummy;
}
}
}
Parallelization)
Stream<Integer> stream = arrayList.parallelStream();
stream.forEach(a -> {
String symbol = StockPriceUtils.makeSymbol(a);
StockPriceHistory sph = new StockPriceHistoryImpl(symbol, startDate, endDate, entityManager);
});



[Java Performance] 线程及同步的性能之线程池/ThreadPoolExecutors/ForkJoinPool的更多相关文章
- Java 线程与同步的性能优化
本文探讨的主题是,如何挖掘出Java线程和同步设施的最大性能. 1.线程池与ThreadPoolExecutor 1)线程池与ThreadPoolExecutor 线程池的实现可能有所不同,但基本概念 ...
- 【转】线程及同步的性能 - 线程池 / ThreadPoolExecutors / ForkJoinPool
线程池和ThreadPoolExecutors 虽然在程序中可以直接使用Thread类型来进行线程操作,但是更多的情况是使用线程池,尤其是在Java EE应用服务器中,一般会使用若干个线程池来处理来自 ...
- 线程及同步的性能 – 线程池/ ThreadPoolExecutors/ ForkJoinPool
线程池和ThreadPoolExecutors 虽然在程序中可以直接使用Thread类型来进行线程操作,但是更多的情况是使用线程池,尤其是在Java EE应用服务器中,一般会使用若干个线程池来处理来自 ...
- 牛客网Java刷题知识点之为什么HashMap不支持线程的同步,不是线程安全的?如何实现HashMap的同步?
不多说,直接上干货! 这篇我是从整体出发去写的. 牛客网Java刷题知识点之Java 集合框架的构成.集合框架中的迭代器Iterator.集合框架中的集合接口Collection(List和Set). ...
- JAVA多线程提高十一:同步工具Exchanger
Exchanger可以在对中对元素进行配对和交换的线程的同步点.每个线程将条目上的某个方法呈现给 exchange 方法,与伙伴线程进行匹配,并且在返回时接收其伙伴的对象.Exchanger 可能被视 ...
- 01 语言基础+高级:1-7 异常与多线程_day06 【线程、同步】
day06 [线程.同步] 主要内容 线程 同步 线程状态 一.学习目标 1. 能够描述Java中多线程运行原理 2. 能够使用继承类的方式创建多线程 3. 能够使用实现接口的方式创建多线程 4. 能 ...
- Windows核心编程 第八章 用户方式中线程的同步(上)
第8章 用户方式中线程的同步 当所有的线程在互相之间不需要进行通信的情况下就能够顺利地运行时, M i c r o s o f t Wi n d o w s的运行性能最好.但是,线程很少能够在所有的时 ...
- 308 day06_线程、同步
day06 [线程.同步] 主要内容 线程 同步 线程状态 教学目标 能够描述Java中多线程运行原理 能够使用继承类的方式创建多线程 能够使用实现接口的方式创建多线程 能够说出实现接口方式的好处 ...
- java线程(2)--同步和锁
参考转载:http://rainyear.iteye.com/blog/1734311 http://turandot.iteye.com/blog/1704027 http://www.cnblog ...
随机推荐
- Java享元模式(Flyweight Pattern)
享元模式(Flyweight Pattern)主要用于减少创建的对象数量,并减少内存占用并提高性能. 这种类型的设计模式属于结构模式,因为该模式提供了减少对象计数的方法,从而改善应用的对象结构. 享元 ...
- UVA12174_Shuffle
Shuffle 大致题意: 你有一个随机播放的播放器,有s首歌,在这s首播放完之前不会重新打乱顺序,现在给出一段只含有1~s的n长度序列,现在问你下次随机排序发生的时间有多少种可能 其实就是问你这个播 ...
- Vue 在手机上键盘把底部菜单顶上去的解决方案
Vue 在手机上键盘把底部菜单顶上去的解决方案 ios和安卓的键盘的区别 ios和安卓的键盘的区别弹起方式不同, ios直接弹出键盘, 不影响页面, 而安卓键盘弹起时会把页面顶起来, 这样就会把底部菜 ...
- SSL certificate problem: self signed certificate
执行Git命令时出现各种 SSL certificate problem 的解决办法 2014年10月11日 10:45:40 比如我在windows下用git clone gitURL 就提示 ...
- 转 zookeeper,dubbo和Nginx的区别
Nginx是著名的反向代理服务器,也被广泛的作为负载均衡服务器 ZooKeeper是分布式协调服务框架,有时也被用来做负载均衡 那么他们的区别是什么?如何选择呢? 下面从实际场景看下他们的关系 Ngi ...
- 创建调用Consul的客户端项目
创建调用Consul的客户端项目 创建项目 源码 Github仓库:https://github.com/sunweisheng/spring-cloud-example
- linux缺頁異常處理--內核空間[v3.10]
缺頁異常被觸發通常有兩種情況—— 1.程序設計的不當導致訪問了非法的地址 2.訪問的地址是合法的,但是該地址還未分配物理頁框 下面解釋一下第二種情況,這是虛擬內存管理的一個特性.盡管每個進程獨立擁有3 ...
- Spring学习笔记(14)——注解零配置
我们在以前学习 Spring 的时候,其所有的配置信息都写在 applicationContext.xml 里,大致示例如下: java代码: <beans> <bean n ...
- react app相关知识
1.快速新建名为hello-world项目的应用命令 npx create-react-app hello-world 2.使用serve来mock数据 ①先安装serve npm i ...
- Java实现按汉语拼音的排序
public class sortByPinyin { public static void main(String[] args) { String[] arr = { "刘刘" ...