浅谈Java两种并发类型——计算密集型与IO密集型
转载:https://blog.csdn.net/u013070853/article/details/49304099
核心是可以分别独立运行程序指令的计算单元。
线程是操作系统能够进行运算调度的最小单位。
PS:4核心8线程的!等于你有4个仓库,你要运输货物,8线程就是高速公路!8条高速公路送比你4条高速公路运的快吧!
有一个原则是:活跃线程数为 CPU(核)数时最佳。过少的活跃线程导致 CPU 无法被充分利用,过多的活跃线程导致过大的线程上下文切换开销。
线程应该是活跃的,处于 IO 的线程,休眠的线程等均不消耗 CPU。
在Java并发编程方面,计算密集型与IO密集型是两个非常典型的例子,这次大象就来讲讲自己在这方面的内容,本篇比较基础,只适合刚入门的童鞋,请各种牛人不喜勿喷。
计算密集型
计算密集型,顾名思义就是应用需要非常多的CPU计算资源,在多核CPU时代,我们要让每一个CPU核心都参与计算,将CPU的性能充分利用起来,这样才算是没有浪费服务器配置,如果在非常好的服务器配置上还运行着单线程程序那将是多么重大的浪费。对于计算密集型的应用,完全是靠CPU的核数来工作,所以为了让它的优势完全发挥出来,避免过多的线程上下文切换,比较理想方案是:
线程数 = CPU核数+1
也可以设置成CPU核数*2,这还是要看JDK的使用版本,以及CPU配置(服务器的CPU有超线程)。对于JDK1.8来说,里面增加了一个并行计算,计算密集型的较理想线程数 = CPU内核线程数*2
计算文件夹大小算是一个比较典型的例子,代码很简单,我就不多解释了。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* 计算文件夹大小
* @author 菠萝大象
*/
public class FileSizeCalc {
static class SubDirsAndSize {
public final long size;
public final List<File> subDirs;
public SubDirsAndSize(long size, List<File> subDirs) {
this.size = size;
this.subDirs = Collections.unmodifiableList(subDirs);
}
}
private SubDirsAndSize getSubDirsAndSize(File file) {
long total = 0;
List<File> subDirs = new ArrayList<File>();
if (file.isDirectory()) {
File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
if (child.isFile())
total += child.length();
else
subDirs.add(child);
}
}
}
return new SubDirsAndSize(total, subDirs);
}
private long getFileSize(File file) throws Exception{
final int cpuCore = Runtime.getRuntime().availableProcessors();
final int poolSize = cpuCore+1;
ExecutorService service = Executors.newFixedThreadPool(poolSize);
long total = 0;
List<File> directories = new ArrayList<File>();
directories.add(file);
SubDirsAndSize subDirsAndSize = null;
try{
while(!directories.isEmpty()){
List<Future<SubDirsAndSize>> partialResults= new ArrayList<Future<SubDirsAndSize>>();
for(final File directory : directories){
partialResults.add(service.submit(new Callable<SubDirsAndSize>(){
@Override
public SubDirsAndSize call() throws Exception {
return getSubDirsAndSize(directory);
}
}));
}
directories.clear();
for(Future<SubDirsAndSize> partialResultFuture : partialResults){
subDirsAndSize = partialResultFuture.get(100,TimeUnit.SECONDS);
total += subDirsAndSize.size;
directories.addAll(subDirsAndSize.subDirs);
}
}
return total;
} finally {
service.shutdown();
}
}
public static void main(String[] args) throws Exception {
for(int i=0;i<10;i++){
final long start = System.currentTimeMillis();
long total = new FileSizeCalc().getFileSize(new File("e:/m2"));
final long end = System.currentTimeMillis();
System.out.format("文件夹大小: %dMB%n" , total/(1024*1024));
System.out.format("所用时间: %.3fs%n" , (end - start)/1.0e3);
}
}
}
执行10次后结果如下:
在上面的例子中,线程池设置为CPU核心数+1个,这个运行结果是大象在工作电脑(CPU:G630 内存:4G JDK1.7.0_51)上跑出来的。如果在这里把线程池加大,比如调到100,你会发现所用时间变多了,大象这里最多的消耗时间是0.297秒,与之前最少的一次0.218之间相差0.079秒,也即79毫秒。当然这多出来的时间在我们看来好像不算什么,只有零点零几秒,但是对于CPU来说可是相当长的,因为CPU里面是以纳秒为计算单位,1毫秒=1000000纳秒。所以加大线程池会增加CPU上下文的切换成本,有时程序的优化就是从这些微小的地方积累起来的。
IO密集型
对于IO密集型的应用,就很好理解了,我们现在做的开发大部分都是WEB应用,涉及到大量的网络传输,不仅如此,与数据库,与缓存间的交互也涉及到IO,一旦发生IO,线程就会处于等待状态,当IO结束,数据准备好后,线程才会继续执行。因此从这里可以发现,对于IO密集型的应用,我们可以多设置一些线程池中线程的数量,这样就能让在等待IO的这段时间内,线程可以去做其它事,提高并发处理效率。
那么这个线程池的数据量是不是可以随便设置呢?当然不是的,请一定要记得,线程上下文切换是有代价的。目前总结了一套公式,对于IO密集型应用:
线程数 = CPU核心数/(1-阻塞系数)
这个阻塞系数一般为0.8~0.9之间,也可以取0.8或者0.9。套用公式,对于双核CPU来说,它比较理想的线程数就是20,当然这都不是绝对的,需要根据实际情况以及实际业务来调整。
final int poolSize = (int)(cpuCore/(1-0.9))
本篇大象简单谈了下并发类型,旨在抛砖引玉,让初学并发编程的朋友能够有一些了解,说的不对的地方,还请各位指出来。
唠叨完上面这些,再唠叨下JDK的版本,每次Java的版本升级,就意味着虚拟机以及GC的性能都有一定程度的提升,所以JDK1.7比JDK1.6在并发处理速度上要更快一些,注意对多线程程度请加上-server参数,并发效果更好一些。现在JDK1.8都出来这么久了,你的JDK是不是应该升级下了呢?
本文为菠萝大象原创,如要转载请注明出处。http://www.blogjava.net/bolo
浅谈Java两种并发类型——计算密集型与IO密集型的更多相关文章
- 再谈java两种变量(基本类型和引用类型)(综合各路大神)
基本类型: 基本类型自然不用说了,它的值就是一个数字,一个字符或一个布尔值. int a: a=250: //声明变量a的同时,系统给a分配了数据空间. 引用类型: 是一个对象类型,值是什么呢? ...
- 浅谈java中的枚举类型(转)
用法一:常量 在JDK1.5 之前,我们定义常量都是: public static fianl.... .现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法. p ...
- 从SqlSessionFactoryBean的引用浅谈spring两种bean模式
mybatis是以一个 SqlSessionFactory 的实例为中心的.SqlSessionFactory可以通过SqlSessionFactoryBuilder获得实例.使用mybatis-sp ...
- java List递归排序,传统方式和java8 Stream优化递归,无序的列表按照父级关系进行排序(两种排序类型)
当有一个List列表是无序的,List中的数据有parentid进行关联,通过java排序成两种排序类型: 所用的测试列表最顶级无parentid,若为特殊值,修改下判断方法即可. 第一种排序:按照树 ...
- 列举两种不同类型的Java标识注释,并解释它们之间的区别。
列举两种不同类型的Java标识注释,并解释它们之间的区别.
- Java中的两种异常类型及其区别?
Java中的两种异常类型是什么?他们有什么区别? Throwable包含了错误(Error)和异常(Excetion两类) Exception又包含了运行时异常(RuntimeException, 又 ...
- 浅谈Java中的equals和==(转)
浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str ...
- 浅谈Java中的equals和==
浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: String str1 = new String("hello"); String str2 = ...
- 浅谈Java的集合框架
浅谈Java的集合框架 一. 初识集合 重所周知,Java有四大集合框架群,Set.List.Queue和Map.四种集合的关注点不同,Set 关注事物的唯一性,List 关注事物的索引列表,Q ...
随机推荐
- POJ 3155 Hard Life 最大密度子图 最大权闭合图 网络流 二分
http://poj.org/problem?id=3155 最大密度子图和最大权闭合图性质很相近(大概可以这么说吧),一个是取最多的边一个是取最多有正贡献的点,而且都是有选一种必须选另一种的限制,一 ...
- bzoj2243 染色
Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段), 如 ...
- Codeforces Round #352 (Div. 2) B. Different is Good 水题
B. Different is Good 题目连接: http://www.codeforces.com/contest/672/problem/B Description A wise man to ...
- Uva 12889 One-Two-Three
Your little brother has just learnt to write one, two and three, in English. He has written a lot ...
- ASP.NET 构建高性能网站 第4篇
部署优化 我们都知道,不同的部署方式对站点的性能是有影响的,可能有些朋友已经知道了这点,不管怎样,我们这里还是详细系统的讲述一下这个问题,熟悉的朋友权当回顾J. Release方式编译项目 如果我们的 ...
- CentOS6.X关闭防火墙
一.关闭防火墙 1.重启后永久性生效: 开启:chkconfig iptables on 关闭:chkconfig iptables off 2.即时生效,重启后失效: 开启:service ipta ...
- 模仿JQuery 的添加多个事件的原理
var jquery=function(dom){ var obj={ ready:function(fn){ if(typeof dom.onload=="function"){ ...
- MYSQL 源码- 淘宝数据库研发组
http://mysql.taobao.org/index.php?title=%E8%B5%84%E6%96%99%E5%85%B1%E4%BA%AB
- 淘宝海量数据库之八-攻克随机IO难关 -----阿里正祥的博客
http://blog.sina.com.cn/s/blog_3fc85e260100qwv8.html
- Python之“可变”的tuple
前面我们看到了tuple一旦创建就不能修改.现在,我们来看一个"可变"的tuple: >>> t = ('a', 'b', ['A', 'B']) 注意到 t 有 ...