文档章节

浅谈Java两种并发类型——计算密集型与IO密集型

H
 Henrykin
发布于 2017/03/16 15:49
字数 1299
阅读 22
收藏 0

在Java并发编程方面,计算密集型与IO密集型是两个非常典型的例子,这次大象就来讲讲自己在这方面的内容,本篇比较基础,只适合刚入门的童鞋,请各种牛人不喜勿喷。
    计算密集型
    计算密集型,顾名思义就是应用需要非常多的CPU计算资源,在多核CPU时代,我们要让每一个CPU核心都参与计算,将CPU的性能充分利用起来,这样才算是没有浪费服务器配置,如果在非常好的服务器配置上还运行着单线程程序那将是多么重大的浪费。对于计算密集型的应用,完全是靠CPU的核数来工作,所以为了让它的优势完全发挥出来,避免过多的线程上下文切换,比较理想方案是:
    线程数 = CPU核数+1
    也可以设置成CPU核数*2,这还是要看JDK的使用版本,以及CPU配置(服务器的CPU有超线程)。对于JDK1.8来说,里面增加了一个并行计算,计算密集型的较理想线程数 = CPU内核线程数*2
计算文件夹大小算是一个比较典型的例子,代码很简单,我就不多解释了。

import java.io.File;
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

本文转载自:http://www.blogjava.net/bolo/archive/2015/01/20/422296.html

H
粉丝 4
博文 102
码字总数 12788
作品 0
广州
私信 提问
《虚拟机并发编程》读书笔记(一)

出差北京,趁回杭州之前去了趟海淀,顺手带了本之前一直想买的虚拟机并发编程,虽然这本书在amazon中国上的评价一般,但看看总还是有些收获吧,可能那些说书一般的都是大神吧。书应该是刚上新...

Gaischen
2013/06/21
546
0
JAVA和Go语言的多线程并发测试二

以前做过一次Go和Java的多线程并发对比测试(Java、Scala和Go语言多线程并发对比测试)。当时,测试所采用的例子是CPU运算密集型的,会占用大量的CPU资源。测试的结果Go并不占优势,可能的原...

qinhui99
2012/06/05
4K
4
性能优化之Java(Android)代码优化

最新最准确内容建议直接访问原文:性能优化之Java(Android)代码优化 本文为Android性能优化的第三篇——Java(Android)代码优化。主要介绍Java代码中性能优化方式及网络优化,包括缓存、异步、...

Trinea
2013/08/26
2.6K
1
大数据教程(8.5)mapreduce原理之并行度

上一篇博客介绍了mapreduce的移动流量分析的实战案例,本篇将继续分享mapreduce的并行度原理。 一、mapTask并行度的决定机制 一个job的map阶段并行度由客户端在提交Job是决定,而客户端对map...

em_aaron
2018/11/27
11
0
MapReduce中map并行度优化及源码分析

mapTask并行度的决定机制   一个job的map阶段并行度由客户端在提交job时决定,而客户端对map阶段并行度的规划的基本逻辑为:将待处理数据执行逻辑切片(即按照一个特定切片大小,将待处理数...

刘洋intsmaze
2017/04/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

JAVA 编写redisUtils工具类,防止高并发获取缓存出现并发问题

import lombok.extern.slf4j.Slf4j;import org.springframework.data.redis.core.BoundHashOperations;import org.springframework.data.redis.core.BoundValueOperations;import org.......

huangkejie
28分钟前
4
0
JMM内存模型(一)&volatile关键字的可见性

在说这个之前,我想先说一下计算机的内存模型: CPU在执行的时候,肯定要有数据,而数据在内存中放着呢,这里的内存就是计算机的物理内存,刚开始还好,但是随着技术的发展,CPU处理的速度越...

走向人生巅峰的大路
45分钟前
90
0
你对AJAX认知有多少(2)?

接着昨日内容,我们几天继续探讨ajax的相关知识点 提到ajax下面几个问题又是必须要了解的啦~~~ 8、在浏览器端如何得到服务器端响应的XML数据。 通过XMLHttpRequest对象的responseXMl属性 9、 ...

理性思考
55分钟前
4
0
正则表达式基础(一)

1.转义 转义的作用: 当某个字符在表达式中具有特殊含义,例如字符串引号中出现了引号,为了可以使用这些字符本身,而不是使用其在表达式中的特殊含义,则需要通过转义符“\”来构建该字符转...

清自以敬
58分钟前
4
0
idea中@Data标签getset不起作用

背景:换电脑以后在idea中有@data注解都不生效 解决办法:idea装个插件 https://blog.csdn.net/seapeak007/article/details/72911529...

栾小糖
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部